GPU Instancer:FAQ

From GurBu Wiki
Revision as of 14:30, 10 February 2019 by GurBu Admin (talk | contribs) (How can I Setup my Instances to have Material Variations on them?)
Jump to: navigation, search

About | Features | Getting Started | Terminology | Best Practices | F.A.Q.

General Questions

What are the Minimum Requirements?

To provide the fastest possible performance, GPU Instancer utilizes indirect GPU instancing using Unity's DrawMeshInstancedIndirect API and Compute Shaders. Following are the minimum requirements for GPU instancing and Compute Shader support:

  • DirectX 11 or DirectX 12 and Shader Model 5.0 GPU (Windows, Windows Store)
  • Metal (macOS, iOS)
  • OpenGL Core 4.3 (Windows, Linux)
  • Vulkan (Android, Windows, Linux)
  • OpenGL ES 3.1 (Android 8.0 Oreo or later)
  • Modern Consoles (PS4, Xbox One)

Please also note that even though some integrated graphics cards may satisfy these requirements, they may not provide the boost you can get from GPU Instancing. Therefore, hardware with a dedicated GPU is recommended.

How does GPU Instancer work?

The aim of GPUI is to provide an easy to use interface to use indirect instancing without having to go through the learning curves of (or extensive development times of) GPU programming and Compute Shaders. To provide this, GPUI analyzes a prefab (or Unity terrain) and uses indirect instancing to render its instances (or detail/tree prototypes). Upon sending the mesh and material data to the GPU once, GPUI creates various GPU buffers and dispatches Compute Shaders on every frame to manipulate the instance data in these buffers. This approach takes the load completely off the CPU and uses the GPU for all rendering processes - so that the CPU threads work more efficiently for game scripts. GPUI also uses different optimization techniques to effectively work on different platforms; so that whether the target platform is a high-end PC, VR, or a modern mobile device, GPUI provides the best indirect instancing strategy.

If you're not familiar with these concepts, you can take a look at the Terminology Page for more information. In any case, here is a basic primer on GPU instancing:

(Indirect) GPU instancing works by sending mesh/material data to the GPU in a single draw call. This reduces the amount of batching to one even if there are hundreds or even thousands of the same mesh/material combination. This removes the bottleneck that occurs when sending mesh and material data from CPU to the GPU, and results in higher fps if there are many of the same mesh-material combinations. That is, GPU instancing helps in scenarios where you have many instances of the same game object.

How is GPUI Different from the Unity Material GPU Instancing Option?

Untiy provides instancing support by using the "Enable GPU instancing" option in Unity Standard and Surface shaders. When you use this option, Unity will handle frustum culling and culling by baked occlusion maps automatically. However, when using this option, Unity will group together instances and issue a draw call for every 500 instances in Direct3D and 125 instances in OpenGL ES 3, OpenGL Core, and Metal. The reason for this limitation is that Unity is aiming to support as many devices as possible, and older devices have a smaller GPU buffer size.

Other options in Unity are the DrawMeshInstanced and the DrawMeshInstancedIndirect scripting APIs. The former allows for 1023 instances per batch, and the latter does not have a limit. However, when using both of these options, you need to manage frustum, occlusion and distance culling for your instances manually.

GPU Instancer uses the indirect instancing method in the background and manages all culling operations in the GPU by making use of Compute Shaders. Furthermore, GPUI gives you a user friendly interface to add indirect instancing support for your Prefabs (with LOD groups, child hierarchies etc.) and your Unity Terrains in a few clicks - without writing any code. Since GPUI automatically generates a copy of the original shader of the material that it configures to work with itself, you are also not limited to using the Standard and Surface shaders and can use most custom shaders out of the box with it.

GPUI also builds additional features on its core by allowing you to use shadows, select shadow LODs, add/remove/update instances automatically, generate billboards and a lot more. Advanced features also include support for a no-game object workflow through an easy to use scripting API.

How can I convert shaders manually to support GPU Instancer?

GPUI has a system that automatically generates a converted version of most shaders to work with itself. This system keeps track of shader changes and handles re-conversions automatically as well. This includes most of the shaders created by shader creation editors like Amplify Shader Editor. But in some special cases, it might be needed to change the shader manually to support GPU Instancer. GPUI detects if a shader is compatible with it and will not generate a separate shader if that is the case. Here is the information you need to manually edit your shaders to support GPUI.

Surface Shaders

SubShader {
    #include "UnityCG.cginc"
    #include "GPUInstancer/Resources/Shaders/Include/GPUInstancerInclude.cginc"
    #pragma instancing_options procedural:setupGPUI

Since the #pragma multi_compile_instancing directive is automatically added to the generated code from surface shaders, you do not need to include it. However, if you have a additional passes (such as a shadowcaster pass), you need to add support to these passes as well (see Vertex/fragment Shaders below)

Vertex/fragment Shaders

For all passes:

Pass {
    #include "UnityCG.cginc"
    #include "GPUInstancer/Resources/Shaders/Include/GPUInstancerInclude.cginc"
    #pragma multi_compile_instancing
    #pragma instancing_options procedural:setupGPUI

You also need to add instancing setup to your input structs and vertex functions if you don't already have it:

struct appdata_custom

v2f vert( appdata_custom v )
    v2f o;
    return o;

SRP Shaders

You need to reference the SRP includes you need and add GPUI support afterwards. Here is an example which uses LWRP:

#include "LWRP/ShaderLibrary/InputSurfacePBR.hlsl"
#include "LWRP/ShaderLibrary/LightweightPassLit.hlsl"
#include "GPUInstancer/Resources/Shaders/Include/GPUInstancerInclude.cginc"
#pragma instancing_options procedural:setupGPUI

Amplify Shader Editor directives

It is important to add support from the ASE window rather than the code since ASE would overwrite your changes if you save the shader again. You can use the following additional directives:

Amplify Shader Editor Directives for GPUI

What are the Build Settings for Android platforms?

Build Settings for Android

GPUI relies heavily on Compute Shaders for its operations in the GPU. In mobile platforms, Compute Shaders are usually supported by the higher-end devices. The requirement for Compute Shaders in the Android systems is at least Open GL ES 3.1 or the Vulkan API.

GPUI does not need any additional settings when targeting Vulkan; however, you need to enable Open GL ES 3.1 support when targeting the Open GL ES API.

To do this, you can simply enable the Require ES3.1 checkbox under:

Edit->Project Settings->Player->Other Settings.

Otherwise Unity will disable the Compute Shaders and your build will run without GPUI enabled.

Please also note that older versions of Unity have some bugs for some of the GPU models on Android devices. If you're working on an Android game, it is recommended to use the latest Unity version.

How can I Increase Billboard Visibility Distances Beyond Manager Limits?

The max billboard distance is tied to the Max Tree Distance property of the manager if you are using the Tree Manager - or if you are using the Prefab Manager, it is tied to the Max Distance property of the prototype. The Tree Manager limits you to 2500 where the Prefab Manager limits to 10000.

If you need more visibility distance, you can edit the limitations of the corresponding managers from the following lines in the GPUInstancerEditorConstants.cs

public static readonly float MAX_TREE_DISTANCE = 2500;
public static readonly float MAX_PREFAB_DISTANCE = 10000;

How can I add Multiple Prototypes to an Existing Prefab Manager?

The Scene Prefab Importer is useful when you are adding a Prefab Manager to the scene for the first time; however it can be a tedious task to add new prototypes one by one once you already have a Prefab Manager in the Scene.

One way to to define multiple prefabs at once to an existing Prefab Manager is to lock the Unity inspector window. You can do this easily with the following steps:


1. Lock the Prefab Manager Inspector window.


2. Select multiple prefabs from Project window.


3. Drag and drop all the selected prefabs on Add button on the Prefab Manager inspector window. This will define all of the dragged prefabs as a prototype.


4. Afterwards, if you wish to change the settings for all of the prototypes at once, you can select the prototype ScriptableObjects under the GPUInstancer/PrototypeData/Prefab folder and change the settings in the Inspector window.

How can I Setup my Instances to have Material Variations on them?

Material Variations on Instances of a Single Prototype

GPU Instancing works by drawing a single mesh and material combination multiple times on the screen. This is why GPUI creates its prototypes from Prefab definitions where it uses the mesh and material information for each renderer the Prefab has and issues drawcalls for them seperately. Thus, out of the box, there would not be any variations for the materials of the prefab instances. However, GPUI offers a solution for having material variations through the GPU Instancer API. The included demo scene ColorVariationsDemo is intended as an example usage of this, so you can take a look at it to get started. Please note, however, that this is an advanced feature and will require you to do some scripting and shader editing.

To setup instance based variations, you basically need to define a buffer, register this buffer to the GPUI prototype instances (and the Prefab Manager) and modify your shader to accept this buffer. The contents of the buffer (variations) can then be set and updated during runtime. Therefore, the setup has a C# scripting part and a shader scripting part:

1. On the MonoBehaviour Script:

Here you can define the buffer and register it to the GPUI instances. You can then update the instances through the GPU Instancer API to make GPUI use this buffer for variations of the instance material. The following example generates a number of GPUI prototype instances, defines a color variations buffer and registers it to the GPUI instances in the Start method. It then updates the buffer with random colors in the Update method, making the generated instances change to a random color every update without breaking instancing. The required references are to the GPUI Prototype to generate instances of (the Prefab itself can be assigned for this since the GPUI prototype definition is on the Prefab) and the active Prefab Manager in the scene.

  2.     public class ColorVariations : MonoBehaviour
  3.     {
  4.         // The reference to the Prototype (the prefab itself can be assigned here since the GPUI Prototype component lives on the Prefab).
  5.         public GPUInstancerPrefab prefab;
  6.         // The reference to the active Prefab Manager in the scene.
  7.         public GPUInstancerPrefabManager prefabManager;
  8.         // The count of instances that will be generated.
  9.         public int instances = 1000;
  10.         // The name of the buffer. Must be the same with the StructuredBuffer in the shader that the Mateiral will use. See: "ColorVariationShader_GPUI.shader".
  11.         private string bufferName = "colorBuffer";
  12.         // The List to hold the instances that will be generated.
  13.         private List<GPUInstancerPrefab> goList;
  14.         void Start()
  15.         {
  16.             goList = <a target="_blank" rel="nofollow" href="">new</a> List<GPUInstancerPrefab>();
  17.             // Define the buffer to the Prefab Manager.
  18.             if (prefabManager != null && prefabManager.isActiveAndEnabled)
  19.             {
  20.                 GPUInstancerAPI.DefinePrototypeVariationBuffer<Vector4>(prefabManager, prefab.prefabPrototype, bufferName);
  21.             }
  22.             // Generate instances inside a radius.
  23.             for (int i = 0; i < instances; i++)
  24.             {
  25.                 GPUInstancerPrefab prefabInstance = Instantiate(prefab);
  26.                 prefabInstance.transform.localPosition = Random.insideUnitSphere * 20;
  27.                 prefabInstance.transform.SetParent(transform);
  28.                 goList.Add(prefabInstance);
  29.                 // Register the variation buffer for this instance.
  30.                 prefabInstance.AddVariation(bufferName, (Vector4)Random.ColorHSV());
  31.             }
  32.             // Register the generated instances to the manager and initialize the manager.
  33.             if (prefabManager != null && prefabManager.isActiveAndEnabled)
  34.             {
  35.                 GPUInstancerAPI.RegisterPrefabInstanceList(prefabManager, goList);
  36.                 GPUInstancerAPI.InitializeGPUInstancer(prefabManager);
  37.             }
  38.         }
  39.         void Update()
  40.         {
  41.             // Update the variation buffer with a random set of colors every frame, thus changing instance colors per instance every frame.
  42.             GPUInstancerAPI.UpdateVariation(prefabManager, goList[Random.Range(0, goList.Count)], bufferName, (Vector4)Random.ColorHSV());
  43.         }
  44.     }

The important parts are as follows:

Defining the name of the varitation buffer (it is important to notice that the name of this buffer must match the StructuredBuffer on the shader exactly):

        private string bufferName = "colorBuffer";

Defining the variation buffer to the Prefab Manager. Please note that you define which type of variation you will use while defining the prototype variation buffer to the manager. In this example, we use a [CODE]Vector4[/CODE] since we are using color variation. This is a generic method and you can read more on it from its API documentation.

        GPUInstancerAPI.DefinePrototypeVariationBuffer<Vector4>(prefabManager, prefab.prefabPrototype, bufferName);

Registering the variation buffer per instance:

        prefabInstance.AddVariation(bufferName, (Vector4)Random.ColorHSV());

And updating the variation buffer later on if you wish:

        GPUInstancerAPI.UpdateVariation(prefabManager, goList[Random.Range(0, goList.Count)], bufferName, (Vector4)Random.ColorHSV());

2. On the Material's Shader:

For this setup to work, you also need the shader that the material will use to recognize the buffer you have defined above. This can be achieved by defining a [CODE]StructuredBuffer[/CODE] property in the shader with exactly the same name as you defined the buffer in the MonoBehaviour script.

You can define the structured buffer among the shader properties as such:

  2.         #if SHADER_API_D3D11
  4.             StructuredBuffer<float4> colorBuffer;
  5.         #endif
  6.         #endif

And then use it as you wish by referencing the instance by its unity_InstanceID from the gpuiTransformationMatrix StructuredBuffer. This is where the instance matrices are stored by GPUI in the GPU. The following example, in line with the color variations example, modifies the Albedo of the surface shader in the surf function with the variations buffer:

  2.         void surf (Input IN, inout SurfaceOutputStandard o) {
  3.             float4 col = _Color;
  4.             #if SHADER_API_D3D11
  6.                     uint index = gpuiTransformationMatrix[unity_InstanceID];
  7.                     col = colorBuffer[index];
  8.             #endif
  9.             #endif
  10.             // Albedo comes from a texture tinted by color
  11.             fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * saturate(col);
  12.             o.Albedo = c.rgb;
  13.             // Metallic and smoothness come from slider variables
  14.             o.Metallic = _Metallic;
  15.             o.Smoothness = _Glossiness;
  16.             o.Alpha = c.a;
  17.         }

The important part here is getting the getting the variation buffer index of the instance by accessing the GPUI instance matrices with the unity_InstanceID:

        uint index = gpuiTransformationMatrix[unity_InstanceID];

And using that index to access the variation in the buffer:

        col = colorBuffer[index];


Instances don't Show / Show at Incorrect Locations when Using a Custom Shader

If you are using a custom vertex/fragment shader, the shader needs to be setup for instancing. GPUI sets shaders up for working with itself, but cannot automate general instancing setup since there is no good way to generalize this.

If you are using a custom shader on your instances, or if you are using an asset from the Asset Store, this will most likely be the reason why your instances do not show correctly. To setup your shaders with GPUI in this case, you can take a look at setting vertex/fragment shaders up manually to work with GPU Instancer.

Shadows on the Prefab Instances Look Blocky/Wrong

Using the Shadow Pass of the Original Shader

By default, GPUI uses a custom, lightweight "shadows only" shader to render the shadows of the prototypes that are defined on a Prefab Manager. Using this shader helps increase the shadow performance slightly more; especially if the original shader is using a shadow pass generated by a surface shader (using the addshadow directive). In most cases this shader is enough to render the prototype flawlessly; however, if the original shader uses vertex animation and/or alpha cutoff, then the shadows might not look right. Typical cases of this are when adding tree prefabs. In these cases you can enable the Shadow with Original Shader option under the Shadow settings for the prototype (in the Prefab Manager). This will use the shadow pass of the original shaders of the prefab and the shadows should then look right.

Batch Counts are Higher than without GPUI

Increased batch counts usually means that there are prototypes with very few instance counts that are registered to the Prefab Manager.

GPUI works best with prefabs that have high instance counts. The reason for this is related to the nature of GPU instancing. Since prefabs with low instance counts will not gain a noticeable performance boost from GPU Instancing, it is usually better to let Unity handle their rendering. Unity uses draw call batching techniques on the background (such as dynamic batching). These techniques depend on the CPU to run and tax their operations on the CPU memory. When there are many instances of the same prefabs, these operations turn out to be too costly and the reduction in batch counts dwarf in comparison to GPU Instancing. But where the instance counts are noticeably low, the cost on the CPU when using these techniques becomes trivial - yet they will still reduce batch and draw call counts. While using GPU instancing, on the other hand, since meshes are not combined, every mesh/material combination will always be one draw call.

In short, the lowest batch counts (and the best performance gain) can be achieved by using GPUI with high instance counts. Therefore, rendering everything with GPUI where some prefabs have very few instance counts is not recommended.

The rule of thumb to keep in mind is to have only the prefabs that have high instance counts rendering with GPUI, while minimizing the amount of prototypes as much as possible. For more information on instance counts, you can check the page for best practices.

Shader Variant not Showing in Build

This can be solved by changing the material on your prefab to use the GPUI version of the shader (e.g. "GPUInstancer/Standard (Specular setup)") instead of the original (e.g. "Standard (Specular setup)"). Alternatively, you can create a dummy material in any /Resources folder that uses the shader in its problematic variant form; this will compile it to the build as well, solving the problem.

The issue here is related to the way Unity handles shader variants. GPUI uses it's own version of the (e.g.) Standard Specular Shader, and switches to this shader at play mode. This shader is in the /resources folder, so it enters the build. However, since you are using the the variant (e.g. cutout) of the original shader, and Unity does not see the GPUI version of this variant on the prefab, it ignores this variant for the GPUI version during build time.

This is true for all the shaders that feature a variant via the "#pragma shader_feature" keyword.

GPUI not working while targeting Android platforms

Disabling OpenGL ES 3.0 Emulation in Android Projects

GPU Instancer requires OpenGL ES 3.1 or Vulkan API for Android platforms. Unity Editor currently can not emulate GLES 3.1. It emulates GLES 3.0 which does not support Compute Shaders. So currently it is not possible to test GPU Instancer with GLES 3.1 at Unity Editor.

If you leave the Emulation mode on, and if you try running your scene with a GPUI manager in it, Unity will throw the following error in the console and GPUI will be deactivated:

Target Graphics API does not support Compute Shaders. Please refer to Minimum Requirements on GPUInstancer/ReadMe.txt for detailed information.

In order to solve this, you can use Unity Editor in No Emulation mode as shown in the picture to the right and make your mobile tests with a build. The editor will then run in the original mode of your operating system (e.g. Direct3D 11 on Windows).

Please also note that Unity switches back to the emulation mode regularly, so you might need to switch back often.

For information on how to use build settings for Android devices you can look at this section.

With Projectors in my Scene, I see Artifacts/Performance Hit

Unity Projectors can cause a performance hit and/or artifacts on the screen when using GPU Instancer. The reason for this is related to how Unity handles projectors internally. There is, however, a very easy solution to workaround this by using Layers. The steps are as follows:

Creating a Layer for GPUI

1) You can start by creating a new Layer (e.g. GPUIRenderingLayer)

Defining the Rendering Layer for Prefabs

2) Setting this Layer to the prefabs are enough for prefab instancing with Prefab Manager, Tree Manager and for the prefab type prototypes in the Detail Manager.

Defining the Rendering Layer in the Manager

3) For the Detail Textures, you can set this new Layer to the Detail Texture Layer of the Detail Manager.

Ignoring the GPUI Rendering Layer in the Projector

4) You can then set the projector to ignore the GPUI Rendering Layer as shown in the picture to the left.

Transperent Materials Look Inside-Out/Back-to-Front when Used on Prototypes

Materials with transparency can result in unexpected behavior when used with GPU Instancer. Since GPU instancing works by treating all instances as the same, the GPU draws one mesh multiple times without any regard to draw ordering. This will result in wrong draw orders while using transparency, and the typical result would be inside-out or back-to-front looking prototype instances. This can be solved by implementing a form of OIT approximation. Please note, however, that GPUI currently does not include such an implementation so if you are willing to use transparent objects with GPUI, you should manually implement an OIT solution.

Compatibility with Other Assets

GPU Instancer has out of the box compatibility with some other Unity Asset Store assets, and also integration with some others. With some assets, however, it will simply not work together because of the nature of the two assets. Assets that rely on Mesh Combining or those that come with their own GPU Instancing solutions fall into this later category.

As Gurbu Technologies, we are always up to integration with other assets. If you are an Asset Store developer, please contact us and we can investigate possible integrations.

In this section, you can find a list of assets that are already known to work or not with GPUI.

Amplify Shader Editor

GPUI works out of the box with ASE shaders. GPUI creates a copy of your ASE shader and configures it to work with itself. Any updates will also be reflected on the created shader. At runtime, GPUI use this converted shader for its prototypes where the material has the original shader. However, if you don't want your shader to be duplicated, you can also check the above description to see how you can manually use the directives necessary for GPUI in your ASE shaders.

Please note, however, that Unity does not support tessellation and GPU instancing at the same time, so tessellated ASE shaders won't work with GPUI as well.

Amplify Impostors

Using a Custom Billboard (Amplify Impostors)

GPUI works out of the box with Amplify Impostors. You can define any Impostor Prefab as a GPUI prototype. GPUI will also use the impostor if it is an a LOD level of the prefab.

Alternatively, you can use the Use Custom Billboard option under the Billboard Settings in all managers and assign the mesh and material that you can generate with Amplify Impostors. This will allow you to keep using GPUI's billboard settings for the selected prototype but render the billboard with Amplify Impostors.

MapMagic World Generator

GPUI - MapMagic Integration

GPU Instancer has a built-in integration with MapMagic World Generator. To use GPUI in your MapMagic Terrains, , you can use the following menu item:

Tools -> GPU Instancer -> Integration -> Add MapMagic Integration

Please note that GPUI checks the scripting define symbols in the player settings to determine whether you have MapMagic installed. If you don't have MapMagic, you will not see this menu item. If you have MapMagic, however, and if you don't see the integration menu item, the scripting define symbol might not be added to your player settings for some reason. In that case, you can manually add it by opening your Unity Player Settings (Edit -> Project Settings -> Player) and entering ;MAPMAGIC at the end of your scripting define symbols line.

When you add the integration from the menu, GPUI will add a GameObject to your scene with the options showing you what you want to import to GPUI from the MapMagic generation ruleset in the scene. You can then check the options you wish and click Import. This will turn the integration object into what looks like a combined version of all the GPUI Managers, and you can use the settings as you normally would in those managers.

This is all you need to do to render your MapMagic terrains with GPUI. If at any time you change or edit the MapMagic ruleset, you can click Import again and the changes will reflect on the integration as well.


GPUI - Gaia Integration

GPU Instancer has a built-in integration with Gaia. If you wish to use Gaia generated terrains with GPUI, you can use the GX tab under the Gaia Manager to add all desired GPUI managers to your scene.

When you add the The Detail and the Tree Managers this way, it is exactly like adding them from the GPU Instancer Menu. They are there only for convenience.

When you use Add the Prefab Manager from the GX menu, however, GPUI shows you a window that is similar to GPUI's Scene Prefab Importer tool. Using this window, however, instead of the prefab instances in your scene, you are presented with prefab instances that come from the Gaia ruleset that that is defined in the scene.

We recommended you to check the Best Practices Page before using Gaia with GPUI - especially the section on Detail instancing - to get the most out of GPUI.


AQUAS works out of the box with GPU Instancer. However, the AQUAS water planes use two Projectors for water caustics - and these projectors cause a performance hit when using GPU Instancer. There is a very easy solution to overcome this; the steps are as follows:

Creating a Layer for GPUI

1) GPUI includes a feature where you can define a layer that you wish to use for the texture details in the Detail Manager. So you can create a new Layer first (e.g. GPUIRenderingLayer)

Defining the Rendering Layer in the Manager

2) Then set this new Layer to the Detail Texture Layer of the Detail Manager. For all prefabs, GPUI uses the layer of the prefab for this purpose.

Aquas Caustics Projectors

3) Finally, you can set this as the Ignore Layer for the AQUAS caustic projectors. There are two of them under the Aquas Water Plane object.

Ignoring the GPUI Rendering Layer in the Projector

4) You can set ignore the GPUI Rendering Layer as shown in the picture to the left.

Vegetation Studio / Vegetation Studio (Pro)

Since Vegetation Studio uses its own rendering system, it is not possible for GPUI and VS to work on the same objects. However, if you wish to use VS and GPUI together, you can still use GPUI for prefabs that you do not define in VS.