Search Unity

How should i work with BatchRendererGroup

Discussion in 'Scripting' started by FARshonok, Feb 2, 2020.

  1. FARshonok

    FARshonok

    Joined:
    Nov 16, 2017
    Posts:
    2
    In case i schedule single batch everything draws correct:


    In case i try to schedule several batched (cube and capsule):


    What i'm doing wrong? May be i incorrect fill matrices array for batches.

    Here is simple script:
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using Unity.Burst;
    4. using Unity.Collections;
    5. using Unity.Jobs;
    6. using Unity.Mathematics;
    7. using UnityEngine;
    8. using UnityEngine.Rendering;
    9.  
    10. public class BatchRenderer : MonoBehaviour
    11. {
    12.     [SerializeField] private GameObject[] prefabs;
    13.     [SerializeField] private int split = 10;
    14.  
    15.     private BatchRendererGroup _batchRendererGroup;
    16.  
    17.     private JobHandle _jobDependency;
    18.  
    19.     private List<int> _batchIndex;
    20.  
    21.     private void OnEnable()
    22.     {
    23.         _batchRendererGroup = new BatchRendererGroup(CullingCallback);
    24.         _batchIndex = new List<int>(10);
    25.  
    26.         foreach (var prefab in prefabs)
    27.             SetupBatch(prefab);
    28.        
    29.     }
    30.  
    31.     private static Mesh GetMesh(Component renderer)
    32.     {
    33.         if (renderer is MeshRenderer)
    34.             return renderer.GetComponent<MeshFilter>().sharedMesh;
    35.        
    36.         var meshRenderer = renderer as SkinnedMeshRenderer;
    37.         if (meshRenderer != null)
    38.             return meshRenderer.sharedMesh;
    39.         throw new InvalidOperationException();
    40.     }
    41.  
    42.     private void SetupBatch(GameObject prefab)
    43.     {
    44.         var materials = new List<Material>(10);
    45.         var renderers = prefab.GetComponentsInChildren<Renderer>();
    46.         foreach (var renderer in renderers)
    47.         {
    48.             renderer.GetSharedMaterials(materials);
    49.            
    50.             var mesh = GetMesh(renderer);
    51.             foreach (var material in materials)
    52.             {
    53.                 _batchIndex.Add(_batchRendererGroup.AddBatch(
    54.                     mesh,
    55.                     0,
    56.                     material,
    57.                     0,
    58.                     ShadowCastingMode.Off,
    59.                     false,
    60.                     false,
    61.                     new Bounds(Vector3.zero, Vector3.one * float.MaxValue),
    62.                     split * split * split,
    63.                     null,
    64.                     gameObject));
    65.             }
    66.         }
    67.     }
    68.  
    69.     private void OnDisable()
    70.     {
    71.         _batchIndex.Clear();
    72.         _batchRendererGroup.Dispose();
    73.     }
    74.  
    75.     private void Update()
    76.     {
    77.         _jobDependency.Complete();
    78.  
    79.         var jobHandlers = new NativeList<JobHandle>(_batchIndex.Count, Allocator.Temp);
    80.         foreach (var batchIndex in _batchIndex)
    81.         {
    82.             jobHandlers.Add(new UpdateMatrixJob
    83.             {
    84.                 Matrices = _batchRendererGroup.GetBatchMatrices(batchIndex),
    85.                 Time = Time.time,
    86.                 Split = split
    87.             }.Schedule(split * split * split, 16));
    88.         }
    89.  
    90.         _jobDependency = JobHandle.CombineDependencies(jobHandlers);
    91.         jobHandlers.Dispose();
    92.     }
    93.  
    94.     private JobHandle CullingCallback(BatchRendererGroup rendererGroup, BatchCullingContext cullingContext)
    95.     {
    96.         _jobDependency.Complete();
    97.         for (var batchIndex=0;  batchIndex<cullingContext.batchVisibility.Length ; ++batchIndex)
    98.         {
    99.             var batchVisibility = cullingContext.batchVisibility[batchIndex];
    100.  
    101.             for (var i = 0; i < batchVisibility.instancesCount; ++i)
    102.             {
    103.                 cullingContext.visibleIndices[batchVisibility.offset + i] = batchVisibility.offset + i;
    104.             }
    105.  
    106.             batchVisibility.visibleCount = batchVisibility.instancesCount;
    107.             cullingContext.batchVisibility[batchIndex] = batchVisibility;
    108.         }
    109.  
    110.         return default;
    111.     }
    112.  
    113.     [BurstCompile]
    114.     private struct UpdateMatrixJob : IJobParallelFor
    115.     {
    116.         public NativeArray<Matrix4x4> Matrices;
    117.         public float Time;
    118.         public float Split;
    119.  
    120.         public void Execute(int index)
    121.         {
    122.             var id = new Vector3(index / Split / Split, index / Split % Split, index % Split);
    123.             Matrices[index] = Matrix4x4.TRS(id * 10,
    124.                 quaternion.EulerXYZ(id + Vector3.one * Time),
    125.                 Vector3.one);
    126.         }
    127.     }
    128. }
     

    Attached Files:

  2. Zec_

    Zec_

    Joined:
    Feb 9, 2017
    Posts:
    148
    Found your thread while searching for information on the BatchRendererGroup (there's so little information out there). I used your code as a base for mine, and after struggling for a while I think I finally got it working and I think I found your issue as well. Your culling is setting the incorrect index.

    You're setting it like this:
    cullingContext.visibleIndices[batchVisibility.offset + i] = batchVisibility.offset + i;


    But it seems like the value that should be set is a local index for the batch. You shouldn't include batchVisibility.offset in the value set. It should be like this:
    cullingContext.visibleIndices[batchVisibility.offset + i] = i;


    My editor kept crashing and rendering in a strange manner before I found this out. I think there are no safety checks for incorrect indices, so the rendering was passed random memory that was out of bounds of the batch matrices.
     
    inspoy_tap4fun likes this.
  3. FARshonok

    FARshonok

    Joined:
    Nov 16, 2017
    Posts:
    2
    Thank you

    I thought if we have global array of indexes we should use indexes with batch offset
     
  4. riveranb

    riveranb

    Joined:
    Jul 16, 2017
    Posts:
    16
    Want to ask a question.
    In example codes, I don't see codes doing any frustum culling.
    Does that mean frustum culling is handled somewhere else automatically?

    Or frustum culling is just not happened in this demo?
     
  5. Hyp-X

    Hyp-X

    Joined:
    Jun 24, 2015
    Posts:
    438
    Unity culls the entire BatchRendererGroup if the combined bounding boxes are off screen.
    Frustum culling of groups / instances of the group should be handled by the culling callback.
    You should also handle LOD during culling which is even more complicated.

    The above simple code just doesn't contain these.
     
    riveranb likes this.
  6. officialfonee

    officialfonee

    Joined:
    May 22, 2018
    Posts:
    44
    I copied the code above and nothing rendered. Does your project have to be special or is there something special you have to do to allow for BatchRendererGroups to work?

    EDIT: Im not sure if you guys were using URP or not, but it seems like the BatchRendererGroup only works in the standard pipline.
     
    Last edited: Dec 31, 2020
  7. Hyp-X

    Hyp-X

    Joined:
    Jun 24, 2015
    Posts:
    438
    It can work with URP but you need to disable SPR batcher (it is somewhere in the pipeline settings)

    In Unity 2020.2 only V2 works with SRP batcher, but I haven't been able to make V2 work with my own shaders... :(
     
  8. officialfonee

    officialfonee

    Joined:
    May 22, 2018
    Posts:
    44
    I see, thanks for the replay. I went with DrawMeshInstancedIndirect because I did an informal performance comparison and DrawMeshInstancedIndirect was faster -- https://forum.unity.com/threads/drawing-from-data-without-hybrid-renderer.820548/#post-6675952
     
  9. SebastianAaltonen

    SebastianAaltonen

    Unity Technologies

    Joined:
    Feb 21, 2020
    Posts:
    112
    I would advice against using BatchRendererGroup API. It's a very low level API that requires deep understanding of Unity shader system internals for correct use. More info here: https://docs.unity3d.com/Packages/c...rid@0.11/manual/batch-renderer-group-api.html

    We are planning to heavily refactor the BatchRendererGroup API. Make it more user friendly and documented. This will however break backwards compatibility with the old API version. I would suggest waiting until we have landed this properly supported API version.
     
    officialfonee likes this.
  10. andrewgotow

    andrewgotow

    Joined:
    Dec 28, 2013
    Posts:
    18
    Thanks Sebastian!
    Is there any timeline you can share on when a refactored BatchRenderGroup API would be ready?

    I'm currently working on a project which needs to render a large number of unique meshes with customized culling and minimal overhead. The BatchRenderGroup API seems to fit this need perfectly!
     
  11. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    727