Thank you for the article! You’ve compared BRG to Graphics.DrawMeshInstanced, but what about Graphics.DrawMeshInstancedIndirect?
Currently I’m using Graphics.DrawMeshInstancedIndirect like this:
// Per Instance properties.
public struct InstancedMeshProperties
{
public float4x4 ObjectToWorld;
public float4x4 WorldToObject;
public Vector4 Color;
public InstancedMeshProperties(float4x4 trs, Color32 color)
{
ObjectToWorld = trs;
WorldToObject = math.inverse(trs);
Color = new Vector4(color.r, color.g, color.b, color.a) / 255f;
}
public static int Size()
{
return sizeof(float) * 4 * 4 + sizeof(float) * 4 * 4 + sizeof(float) * 4;
}
}
// Job to write data to gpu.
[BurstCompile]
public struct WriteToGPU : IJob
{
[ReadOnly] public NativeList<Instance> Instances;
[ReadOnly] public NativeArray<InstancedMeshProperties> MeshProperties;
[WriteOnly] public NativeArray<InstancedMeshProperties> GPUBuffer;
public void Execute()
{
for (int i = 0; i < Instances.Lenght; i++)
{
GPUBuffer[i] = MeshProperties[Instances[i].ID];
}
}
}
// Create buffer for max visible instances.
_buffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured,
GraphicsBuffer.UsageFlags.LockBufferForWrite, _maxInstancesVisibleInRuntime,
InstancedMeshProperties.Size());
// Write to GPU with LockBufferForWrite mechanism to prevent SetData.
new WriteToGPU()
{
Instances = instances,
MeshProperties = _instancedMeshProperties,
GPUBuffer = _buffer.LockBufferForWrite<InstancedMeshProperties>(0, _maxInstancesVisibleInRuntime)
}.Schedule(culling);
// Unlock buffer and draw.
_buffer.UnlockBufferAfterWrite<InstancedMeshProperties>(instancesCount);
Graphics.DrawMeshInstancedIndirect(_mesh, 0, _material, Bounds, _argsBuffer, camera: cameraValue,castShadows: ShadowCastingMode.Off, lightProbeUsage: LightProbeUsage.Off);
//In shader update matrices and color.
void vertInstancingSetup()
{
#ifndef SHADERGRAPH_PREVIEW
#if UNITY_ANY_INSTANCING_ENABLED
unity_ObjectToWorld = mul(unity_ObjectToWorld, _Properties[unity_InstanceID].ObjectToWorld);
unity_WorldToObject = mul(unity_WorldToObject, _Properties[unity_InstanceID].WorldToObject);
#endif
#endif
}
void GetInstancedColor_float(out half4 result)
{
result = half4(0,0,0,0);
#ifndef SHADERGRAPH_PREVIEW
#if UNITY_ANY_INSTANCING_ENABLED
result = _Properties[unity_InstanceID].Color;
#endif
#else
result = half4(1,1,1,1);
#endif
}
For each visible instance I’m writing to GPU a lot of bytes(InstancedMeshProperties struct), but if I change the previous code to two buffers like described in the article - a persistent buffer with data for all instances and another buffer with visible ids of instances that I’ll update with LockBufferForWrite mechanism - will this be slower than BRG? Am I right that If I set data once for this persistent buffer it will stay in GPU memory and I’ll too have GPU persistency like in BRG?
When a scene is rendered using both SRP batched rendering for some objects and BRG rendering for other objects am I right that transparent objects of BRG batches can only be rendered before or after SRP batched transparent objects - we can’t sort them together in a correct back to front order?
Can we use LockBufferForWrite mechanism with BRG?
Also in your example used single GraphicsBuffer, but don’t we need to use Ring Buffer(array of GraphicsBuffers were every frame we write data to the next buffer) to prevent writing from CPU to buffer that currently used by GPU for rendering? I’m doing this for Graphics.DrawMeshInstancedIndirect in the showed code I just omitted usage of buffers for clarity.
Does BRG support dynamic(additional) lights in URP’s Forward or BRG supports them only in URP’s Forward+?