Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Official New BatchRendererGroup API for 2022.1

Discussion in 'Graphics for ECS' started by joelv, Jan 26, 2022.

  1. bnmguy

    bnmguy

    Joined:
    Oct 31, 2020
    Posts:
    137
    When it is used as an excuse, it's a problem.
     
  2. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    352
    Apologies for the slow reply, I was on vacation when this post was made.

    BatchRendererGroup itself does not currently automatically provide correct lightmaps, light probes, or reflection probes to rendered instances. Instead, the responsibility is on the BRG user (such as the Entities Graphics package) and the active SRP to handle these in the following manner:
    • Lightmaps are expected to be provided manually in texture arrays. Normal Unity lightmaps are not texture arrays, and as such switching them requires breaking up draw calls. In addition, BatchRendererGroup does not currently have enough information available CPU side to figure out which instances are using which lightmaps. The Entities Graphics package, for example, manually packs lightmaps in arrays, so instances that are using several lightmaps can be rendered together in a single draw (other batching considerations permitting), and then sets up the unity_LightmapIndex property to contain the correct texture array index to use for each instance.
    • Light probes are expected to be provided by the BRG user (or handled automatically by the SRP, but as far as I know this would require a custom SRP). BRG does not currently always have the necessary instance position information available to interpolate light probes, and even if it did, it would not be able to efficiently cache the results. The Entities Graphics package evaluates interpolated light probe coefficients using LightProbes.CalculateInterpolatedLightAndOcclusionProbes and overrides the unity_SHCoefficients property to contain the interpolated values. This interpolation is only done when the positions change, so the results will be reused if the entities don't move.
    • Reflection probes currently rely completely on the SRP, which means that they will work in HDRP, or when using URP Forward+ (which evaluates reflection probes in screen space). Other URP modes do not currently support reflection probes when BRG is used.
    BRG itself requires support for instanced rendering and shader integer operations, and it should work on GLES 3.0 and up. On GLES it will use UBOs instead of SSBOs, as SSBOs from vertex shaders are not universally supported on GLES. However, the GPU performance can greatly depend on the device, as many low end Android devices are not favorable towards instanced rendering in general, due to instance properties becoming "varying" values instead of "uniform" values.

    The Entities Graphics package additionally requires compute shader support, but that is not a requirement for custom BRG usage.

    Currently, Unity does not have a mechanism to configure how shaders are compiled, so there is no built in way to configure this. However, it is possible to use a locally modified package and comment out properties that you know you don't need. In addition, Shader Graphs provide a way to configure which properties are to be DOTS instanced on a per property basis, without requiring package modifications.

    In general, customizing the package would also allow you to simplify or change how other pieces of the data are loaded. For these kinds of custom optimizations, you would be able to arrange things exactly according to what your game needs, and you would not be restricted by having to support a very wide variety of configurations.

    If you are willing to write custom shaders, it should be possible to highly customize the data with the current BatchRendererGroup API in 2022.3. BatchRendererGroup itself does not really mandate a particular way of loading data, it just provides you a mechanism to efficiently issue instanced draws that have certain buffer bindings set up (the per batch SSBO, and the metadata UBOs with the metadata values for that batch). Unity shaders all use the documented convention how these are used to load instance properties, but a custom shader can do anything it wants with them (and could also use other globally bound textures or SSBOs).

    Currently, you still need to split things by Mesh. Procedural support is something that we have been looking at, but is not available in the current version. However, on most platforms, switching Meshes should be relatively fast, cheaper than switching Materials.
     
    ekakiya and apkdev like this.
  3. dannyalgorithmic

    dannyalgorithmic

    Joined:
    Jul 22, 2018
    Posts:
    100
    Could we use this for rendering (possibly lit) sprites? If so, how?
     
    mihaelys likes this.
  4. yzhuangaa

    yzhuangaa

    Joined:
    Feb 27, 2019
    Posts:
    22
    Just wonder for the old BatchRendererGroup api in 2021.3, is it okay to run in opengles platform. Actually i just need a graphic.drawmeshpersist api,which was promised but latter cancled. And currently it seems only instancing shader can use BatchRendererGroup in 2021.3.
     
  5. someunnameddev

    someunnameddev

    Joined:
    Jul 14, 2023
    Posts:
    9
    Anyone managed to get it to work properly on mobile, and/or has a sample project for that?
    I'm getting weird results where only some instances are displayed - often at lower instance count, not even when pushing the thing to its limits.
    Work perfectly fine on Desktop - it's only mobile that's whacky... but outside of setting GetConstantBufferOffsetAlignment/GetConstantBufferMaxWindowSize I'm not sure if there is any specific code that need to be added/tweak for phones.
     
  6. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    735
    Should work fine on mobile. Would recommend Vulkan over GL ES though. Also, mobile is more strict, there are weird shader tricks to make sure things function properly. Don't recall everything atm, make sure you are using all the build in unity macros and urp functions, don't try to get tricky with it at first. We have to do some weird tricks like referencing the metadata buffer and multiplying by 0.00001 even if we don't need it, etc. But definitely does work on mobile if setup properly.
     
  7. someunnameddev

    someunnameddev

    Joined:
    Jul 14, 2023
    Posts:
    9
    I can get it to work. Mostly. The problem is that on some phones the little benchmark scene I made sometime just drop 3/4 of the models without raising any errors.

    Also the performances are weird:
    Graphics.DrawMeshInstanced is just as fast as BRG on mobile, despite being nearly twice slower on desktop.
    Remade the same scene in another engine, and their local equivalent (MultiMeshInstance3D) is nearly twice as fast as BRG on mobile, despite being much, much slower in desktop.
    Feels like BRG is doing something very wrong with its mobile implementation, it should be faster than anything else.

    Vulkan isn't much of an option sadly since I'm targeting lower-end mobiles and a lot of them (including all the ones in my household) don't support it yet.
     
  8. VincentBreysse

    VincentBreysse

    Unity Technologies

    Joined:
    May 31, 2021
    Posts:
    27
    Many people are talking about mobile development with BRG in this thread. So I thought it might be good to give more visibility to the new "Advanced DOTS Instanced properties usage" section in the BatchRendererGroup manual https://docs.unity3d.com/Manual/dots-instancing-shaders.html

    It's available with 2023.2+ and got backported to 2022 LTS. It's basically giving more control over the way material properties are loaded and can lead to significant performance improvements on low-end platforms when using stock shaders like URP/Lit.

    Especially, if you do not use instanced material properties at all, I would recommand looking into the shader define UNITY_DOTS_INSTANCED_PROP_OVERRIDE_DISABLED_BY_DEFAULT. On Quest 2, I was able to get a 15% reduction of the total GPU frame time on some basic scenes by enabling it.

    Note that ShaderGraph shaders are not impacted by this define. They are already fast in this regard and doing the right thing by default.
     
    Last edited: Oct 19, 2023
  9. larsbertram1

    larsbertram1

    Joined:
    Oct 7, 2008
    Posts:
    6,911
    great - but maybe renaming the section adding "optimized" will help :)
    i know this is not the right place but the entire documentation could need a revamp:
    • the first and most prominent item on each page is the nav to go back or forward - so to skip the page. how bad is this?
    • there is no toc (which would help in this case)
    • there are no info boxes, tips, warnings - all the stuff you know from almost any system be it confluence or notion.
     
  10. swantonb

    swantonb

    Joined:
    Apr 10, 2018
    Posts:
    174
    hi there! Would you mind explaining further what you mean with this: UNITY_DOTS_INSTANCED_PROP_OVERRIDE_DISABLED_BY_DEFAULT
     
  11. Arithmetica

    Arithmetica

    Joined:
    Feb 11, 2019
    Posts:
    44
    Excellent work.
    Do you have any plans to integrate these results with ParticleSystem?
    Currently ParticleSystem requires a particle-specific instancing shader for mesh rendering (not a standard GPU instancing feature). Using a non-supported shader is not very practical due to the large number of vertex transformations performed on the CPU.
    Although technically similar to DOTS instancing, the only standard shaders in URP that support particle-specific instancing are "Particles/Lit", "Particles/Simple Lit", and "Particles/Unlit", which also do not support shadow casting.
    Many developers would be pleased if ParticleSystem would support DOTS instancing rendering, as it would allow the use of many standard URP shaders and shaders created with the shader graph.
     
  12. viruseg

    viruseg

    Joined:
    Jul 8, 2017
    Posts:
    23
    How can I unambiguously check that the current environment where the game is running supports the BatchRendererGroup API? I've searched all the documentation, but I haven't found an answer to my question. There is a list of supported platforms in the documentation. It's fine, but it's completely useless. Today it is such a list and tomorrow it will be added and changed. You just need an api that will unambiguously return true/false. Something like SystemInfo.supportsBRG. Maybe I was not attentive enough and it already exists? If not, please ask the developers to add it.
     
  13. iamarugin

    iamarugin

    Joined:
    Dec 17, 2014
    Posts:
    887
    How to disable invoke OnPerformCulling for certan camera? I have a camera to bake some mesh parameters on a game start. I am calling .Render for this camera in a loop. Because of this I have a bunch of exceptions in the console:

    Code (CSharp):
    1. InvalidOperationException: The previously scheduled job LocalToWorldSystem:ComputeRootLocalToWorldJob writes to the ComponentTypeHandle<Unity.Transforms.LocalToWorld> ComputeRootLocalToWorldJob.JobData.LocalToWorldTypeHandleRW. You are trying to schedule a new job EmitDrawCommandsJob, which reads from the same ComponentTypeHandle<Unity.Transforms.LocalToWorld> (via EmitDrawCommandsJob.LocalToWorld). To guarantee safety, you must include LocalToWorldSystem:ComputeRootLocalToWorldJob as a dependency of the newly scheduled job.
    2. Unity.Jobs.LowLevel.Unsafe.JobsUtility.ScheduleParallelForDeferArraySize (Unity.Jobs.LowLevel.Unsafe.JobsUtility+JobScheduleParameters& parameters, System.Int32 innerloopBatchCount, System.Void* listData, System.Void* listDataAtomicSafetyHandle) (at <5879d8d225474494bcffdfe64965038e>:0)
    3. Unity.Jobs.IJobParallelForDeferExtensions.ScheduleInternal[T] (T& jobData, System.Int32 innerloopBatchCount, System.Void* forEachListPtr, System.Void* atomicSafetyHandlePtr, Unity.Jobs.JobHandle dependsOn) (at ./Library/PackageCache/com.unity.collections@2.1.4/Unity.Collections/Jobs/IJobParallelForDefer.cs:211)
    4. Unity.Jobs.IJobParallelForDeferExtensions.Schedule[T] (T jobData, System.Int32* forEachCount, System.Int32 innerloopBatchCount, Unity.Jobs.JobHandle dependsOn) (at ./Library/PackageCache/com.unity.collections@2.1.4/Unity.Collections/Jobs/IJobParallelForDefer.cs:177)
    5. Unity.Rendering.IndirectListExtensions.ScheduleWithIndirectList[T,U] (T jobData, Unity.Rendering.IndirectList`1[T] list, System.Int32 innerLoopBatchCount, Unity.Jobs.JobHandle dependencies) (at ./Library/PackageCache/com.unity.entities.graphics@1.0.16/Unity.Entities.Graphics/DrawCommandGeneration.cs:783)
    6. Unity.Rendering.EntitiesGraphicsSystem.OnPerformCulling (UnityEngine.Rendering.BatchRendererGroup rendererGroup, UnityEngine.Rendering.BatchCullingContext cullingContext, UnityEngine.Rendering.BatchCullingOutput cullingOutput, System.IntPtr userContext) (at ./Library/PackageCache/com.unity.entities.graphics@1.0.16/Unity.Entities.Graphics/EntitiesGraphicsSystem.cs:1589)
    7. UnityEngine.Rendering.BatchRendererGroup.InvokeOnPerformCulling (UnityEngine.Rendering.BatchRendererGroup group, UnityEngine.Rendering.BatchRendererCullingOutput& context, UnityEngine.Rendering.LODParameters& lodParameters, System.IntPtr userContext) (at <5879d8d225474494bcffdfe64965038e>:0)
    8. UnityEngine.Camera:Render(Camera)
    But I don't need this camera to perform entities culling. So how to disable it?
     
  14. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    352
    I don't think you can completely disable the Camera for BRG, but you can compare the BatchCullingContext.viewID against your own opt-in (or opt-out) list, and early out from the callback in that case.
     
  15. someunnameddev

    someunnameddev

    Joined:
    Jul 14, 2023
    Posts:
    9
    Is there any in-depth documentation about "LockBufferForWrite" usage? Because it's buggy as hell for me, but only in specific situations.
    It cause a lot of wave-like flickering, in semi-stable patterns. But that only happen on DX12. Worst part is, I don't even need to use the actual Lock/Unlock system, just setting the GraphicBuffer's UsageFlags is enough to get some flickering with SetData - actually using Lock/Unlock just had way more. UsageFlag at none and SetData (the old & slow method) cause no flickering. Switching to DX11 also solve the problem.
    Where it get weirder is that adding a second camera (for splitscreen purpose) remove the flickering on the first camera, and cause the second one to flicker like mad. Activating Unity's internal recorder also seem to remove flickering.

    All my tests show that no writing/reading happen during Lock/Unlock, so I'm not sure what I'm doing wrong.
    Are you supposed to do something more complex than Lock/CopyTo/Unlock to get the feature to work stably on DX12?
     
  16. Arithmetica

    Arithmetica

    Joined:
    Feb 11, 2019
    Posts:
    44
    From the indications, it seems to be a typical Read/Write hazard.

    The documentation states that SetData is only available when UsageFlags.None is set, when UsageFlags.LockBufferForWrite is set, some locking is omitted, so the symptoms are not inexplicable.
    https://docs.unity3d.com/2023.3/Documentation/ScriptReference/GraphicsBuffer.UsageFlags.html
     
    Last edited: Dec 15, 2023
  17. someunnameddev

    someunnameddev

    Joined:
    Jul 14, 2023
    Posts:
    9
    Read/Write problem was my guess too, but I don't see how it could happen - which is why I'm wondering if the thing isn't doing more complex stuff under the hood.
    Code (CSharp):
    1.  
    2. NativeArray<Vector4> LockBuffer = this.GraphicsBuffer.LockBufferForWrite<Vector4>(0, this.GraphicsBuffer_RawContent.Length);
    3. this.GraphicsBuffer_RawContent.CopyTo(LockBuffer);
    4. this.GraphicsBuffer.UnlockBufferAfterWrite<Vector4>(this.GraphicsBuffer_RawContent.Length);
    Putting traces around this part and the parts of the code that push data into the "RawContent" buffer show they don't interweave, so there should be no way for anyone to mess with the Lock/Write operation. Tried putting C# locks on RawContent, tried moving the Lock/Write operation around in OnPerformCulling or in Monobehavior.Update, etc.
     
  18. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    352
    If you use LockBufferForWrite, you must manually ensure that the GPU is no longer using the buffer when you update its data again. This is most easily done by triple buffering: you have three buffers, and you cycle which one you use each frame. Otherwise data hazards can happen, and they often show up as flickering.
     
    Arithmetica likes this.
  19. someunnameddev

    someunnameddev

    Joined:
    Jul 14, 2023
    Posts:
    9
    Ah. From the name I thought the lock/unlock system was actually stopping execution until the system was done with the transfer.
    Is there a way to cleanly detect if the GPU is still using a buffer, or is everyone just using 3 buffers to have guaranteed margins? Would double-buffering be good enough in most cases, and the third one is just luxury security?
    Because I'm working with mobiles so limiting RAM usage to the strict minimum would be nice.

    Edit: Also what's the proper method to triple buffer the GraphicsBuffer?
    Call BatchRendererGroup.AddBatch several times at creation (with different buffer handles), then rotate which BatchID you give to the DrawCommands inside OnPerformCulling?
    With BatchRendererGroup I can only get my hand on very basic/superficial example projects, so anything vaguely complex require a lot of shooting the dark with this API.
     
    Last edited: Dec 15, 2023
  20. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    352
    You can jury rig a GPU fence using async readback requests (readback completes = the frame it was in is done), but that can cause problems with preventing devices from entering low power mode.

    I think 3 should be safe on all platforms. 2 is probably safe on some, but not all, and can also depend on settings (e.g. QualitySettings.maxQueuedFrames). Entities Graphics uses 4 just to be on the safe side, but it uses granular buffer pooling so it doesn't actually duplicate everything 4x.

    For rendering (the thing you pass to AddBatch), it is best to use a regular GraphicsBuffer, because LockBufferForWrite compatible buffers might be significantly slower to use on the GPU, as they might be allocated from different memory and/or with different flags in order to support CPU access. Entities Graphics uses a compute shader to copy from the multi-buffered LockBufferForWrite buffers into the actual rendering buffer.
     
    Arithmetica likes this.
  21. someunnameddev

    someunnameddev

    Joined:
    Jul 14, 2023
    Posts:
    9
    Thanks, just added a rotation of BatchID/GraphicsBuffer at it fixed the flickering. Two buffers seems enough in my case, but I guess I will leave triple/quadruple as a user-accessible option just in case other GPUs behave differently.

    Unrelated problem but still with BatchRendererGroup: when starting "Play" mode, if you swap to another window / alt-tab while waiting for it to actually launch, Unity will start freezing *really* hard. As in "everything is locked and manual computer restart is needed". There is a few seconds window during which it will only create a big lag spike, if you come back fast enough.

    This problem doesn't happen in Built mode, thankfully, so I just ignored it as the Editor being the Editor, but is there an actual reason for it and/or a way to not have this behavior? I had that on both this project and the previous one with BRG.
     
  22. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    352
    This is not a known bug, but it does sound like a bug. Please submit a bug report with a repro project if possible.