Search Unity

Updating a ComputeBuffer every frame

Discussion in 'General Graphics' started by Afropenguinn, Jun 23, 2020.

  1. Afropenguinn

    Afropenguinn

    Joined:
    May 15, 2013
    Posts:
    305
    Trying to render mass amounts of sprites using ECS and Graphics.DrawMeshInstancedProcedural. Rendering one million sprites actually ran pretty fast, even with the ComputeBuffer being set every frame (to simulate the cost of sprites moving). The problem came when I tried to simulate what multiple sprite atlases would do. Since multiple sprite atlases would require multiple materials, I simulated it by just rendering multiple "Batches" of sprites with Graphics.DrawMeshInstancedProcedural. Here is the code:

    Code (CSharp):
    1. for (int i = 0; i < Batches; i += 1)
    2. {
    3.     this.transformBuffer[i]?.Release();
    4.     this.transformBuffer[i] = new ComputeBuffer(Instances, 16);
    5.     this.transformBuffer[i].SetData(this.transforms[i]);
    6.     this.materialPropertyBlock.SetBuffer(this.transformBufferId, this.transformBuffer[i]);
    7.  
    8.     this.sizeBuffer[i]?.Release();
    9.     this.sizeBuffer[i] = new ComputeBuffer(Instances, 8);
    10.     this.sizeBuffer[i].SetData(this.sizes[i]);
    11.     this.materialPropertyBlock.SetBuffer(this.sizeBufferId, this.sizeBuffer[i]);
    12.  
    13.     this.uvBuffer[i]?.Release();
    14.     this.uvBuffer[i] = new ComputeBuffer(Instances, 16);
    15.     this.uvBuffer[i].SetData(this.uvs[i]);
    16.     this.materialPropertyBlock.SetBuffer(this.uvBufferId, this.uvBuffer[i]);
    17.  
    18.     this.colorBuffer[i]?.Release();
    19.     this.colorBuffer[i] = new ComputeBuffer(Instances, 16);
    20.     this.colorBuffer[i].SetData(this.colors[i]);
    21.     this.materialPropertyBlock.SetBuffer(this.colorBufferId, this.colorBuffer[i]);
    22.  
    23.     Graphics.DrawMeshInstancedProcedural(
    24.     this.spriteMesh,
    25.     0,
    26.     MaterialTest.Instance.material,
    27.     new Bounds(new Vector2(0.0f, 0.0f), new Vector2(1.0f, 1.0f)),
    28.     Instances,
    29.     this.materialPropertyBlock,
    30.     ShadowCastingMode.Off,
    31.     false,
    32.     0,
    33.     null,
    34.     LightProbeUsage.Off,
    35.     null);
    36. }
    (transforms, sizes, uvs, and colors are just some randomized values for testing)

    The problem is all my different batches keep trying to draw on top of each other. It looks similar to z-fighting, but I don't think it is, because the problem went away when I set the ComputBuffers once, and updated only the materialPropertyBlock every frame. But of course in a live game the ComputerBuffers will have to be updated every frame, so I'm not sure how to stop the "z-fighting" from happening.

    EDIT: Actually, am I correct in thinking that creating a new ComputeBuffer is essentially allocating memory on the GPU? If that's the case, would it hurt just to set some pretty high limits, like setting all 4 CombuteBuffers to have 10,000 count, setting a Batch size limit of 100, and only creating the compute buffers once? From my testing this means about 64MB will be reserved on the GPU at all times, but the system now runs flawlessly and even faster than before since it's not repeatedly disposing and creating ComputeBuffers. And I suppose if you didn't want arbitrary limits, you could always check to see if you exceed your capacity, and only then allocate more space. May have answered my own post here, but some confirmation would be appreciated :)
     
    Last edited: Jun 23, 2020
  2. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    I agree that it is generally better to avoid creating/disposing ComputeBuffers every frame.