Search Unity

Question texture.Apply when using raw texture pointers is doing crazy things in the profiler

Discussion in 'Universal Render Pipeline' started by laurentlavigne, Oct 17, 2021.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    RawTextureData is supposed to be the faster leaner version of making texture.
    But here is what i'm getting when apply(false) is called:
    upload_2021-10-17_11-43-3.png
    then i commented out this line
    upload_2021-10-17_11-43-23.png
    and bam smooth and low
    upload_2021-10-17_11-43-14.png
    this is captured on the switch, deep profiler
    so is this a bug in profiler? Raw being pretty bad? me calling apply wrong?

    here is the script

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Experimental.Rendering;
    5. using UnityEngine.VFX;
    6.  
    7. [DefaultExecutionOrder(-9999)]
    8. public class VFXBatchController : MonoBehaviour
    9. {
    10.     Texture2D _tex; // RGB for position, A for lifetime
    11.     static readonly int countID = Shader.PropertyToID("count");
    12.     static readonly int offsetID = Shader.PropertyToID("offset");
    13.     static readonly int positionsID = Shader.PropertyToID("positionslifetimes");
    14.     const int MAXCOUNT = 1000;
    15.     [Serializable]
    16.     public struct VFX
    17.     {
    18.         public int groupID;
    19.         public VisualEffect vfx;
    20.     }
    21.     public VFX[] vfx;
    22.  
    23.     void OnEnable() { _tex = new Texture2D(MAXCOUNT, 1, GraphicsFormat.R32G32B32A32_SFloat, TextureCreationFlags.None); }
    24.  
    25.     Vector4 _tmp = new Vector4();
    26.  
    27.     void Update()
    28.     {
    29.         var positionsLifetimes = _tex.GetRawTextureData<Vector4>();
    30.         int offset = 0;
    31.         for (int vfxIndex = 0; vfxIndex < vfx.Length; vfxIndex++)
    32.         {
    33.             vfx[vfxIndex].vfx.SetInt(offsetID, offset);
    34.             var countEmitter = VFXBatchEmitter.emitters.ContainsKey(vfx[vfxIndex].groupID) ? VFXBatchEmitter.emitters[vfx[vfxIndex].groupID].Count : 0;
    35.             vfx[vfxIndex].vfx.SetInt(countID, countEmitter);
    36.             if (countEmitter == 0)
    37.                 continue;
    38.             var emitters = VFXBatchEmitter.emitters[vfx[vfxIndex].groupID];
    39.             for (var emitterIndex = 0; emitterIndex < countEmitter; emitterIndex++)
    40.             {
    41.                 var emitter = emitters[emitterIndex];
    42.                 if (emitter.lifetime > 0)
    43.                 {
    44.                     var tp = emitter.transform.position;
    45.                     _tmp.Set(tp.x, tp.y, tp.z, emitter.lifetime);
    46.                     positionsLifetimes[offset + emitterIndex] = _tmp;
    47.                 }
    48.             }
    49.             offset += countEmitter;
    50.         }
    51.         _tex.Apply(false);
    52.         for (int i = 0; i < vfx.Length; i++)
    53.         {
    54.             vfx[i].vfx.SetTexture(positionsID, _tex);
    55.         }
    56.     }
    57. }
     
  2. NicoLeyman

    NicoLeyman

    Unity Technologies

    Joined:
    Jun 18, 2019
    Posts:
    33
    RawTextureData (and Get/SetPixelData) gives you access to the underlying CPU data of the texture.
    Calling Texture.Apply is what actually (re)uploads the CPU data to the GPU. This is an expensive operation.

    For your use case you should be better off using a ComputeBuffer instead of a Texture.
    https://docs.unity3d.com/ScriptReference/ComputeBuffer.BeginWrite.html
    ComputeBuffer.BeginWrite can give you a NativeArray pointing to CPU writable GPU memory which should be faster.
    (this does depend on the hardware, although this should still be the fastest upload path)
    Depending on your use case you could also use BeginWrite to do only a partial update of the buffer each frame.
     
    Last edited: Oct 19, 2021
    laurentlavigne likes this.
  3. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Thanks @NicoLeyman I was wondering about compute buffer, I'm currently using a texture to send data to vfx graph as you can see. How would I have to change the graphs to support compute buffers instead? And any code example on how to write the data to it?

    Also are you saying that using texture.Apply after settings up rawtexturedata nullifies the benefit of rawtexturedata in term of performance?
     
  4. NicoLeyman

    NicoLeyman

    Unity Technologies

    Joined:
    Jun 18, 2019
    Posts:
    33
    RawTextureData and Get/Set PixelData are the fastest ways to access the CPU side data of a Texture. To get your changes onto the GPU you will have to call Texture.Apply which is slow because it does a CPU to GPU transfer.

    I'm not very familiar with VFX graph but it should be possible to access compute buffers from VFX graph as that should be what it's using under the hood as well. Best to look at the VFX graph docs for that.

    I'll file report regarding the ComputeBuffer documentation as it's a bit lacking.
     
    laurentlavigne likes this.
  5. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Thanks for the details, I'll see what the VFX graph entry points are.
     
  6. NicoLeyman

    NicoLeyman

    Unity Technologies

    Joined:
    Jun 18, 2019
    Posts:
    33
  7. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Not in 8.3 it seems, which is the version I use.
    What's missing in the documentation is the version that a feature (page) was added, Like the unreal doc.

    upload_2021-10-20_3-9-52.png