Search Unity

  1. Are you interested in providing feedback directly to Unity teams? Sign up to become a member of Unity Pulse, our new product feedback and research community.
    Dismiss Notice

Set a single float in a FloatArray on a shader

Discussion in 'Shaders' started by wannabee, Apr 21, 2021.

  1. wannabee

    wannabee

    Joined:
    Dec 18, 2012
    Posts:
    3
    In shader code I have a float array:

    Code (CSharp):
    1. float _X[250];
    Doing this in a script then works fine:

    Code (CSharp):
    1. material.SetFloatArray("_X", x);
    However, for each update I only want to change a single value of the array.
    I tried doing this:

    Code (CSharp):
    1. material.SetFloat("_X[" + i.ToString() + "]", x[i]);
    but that naturally didn't work.

    Is there any work around? It seems silly to have to transfer a full array every frame if only one value changes, but I can't see any other way at the moment.

    Thank you.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    10,900
    You have to update the entire array.

    In Unity 5.3 and earlier you could set individual elements of an array, but you also couldn't set the entire array at once. This was done with
    material.SetFloat("_PropertyName" + i.ToString(), values[i]);
    ; note no brackets. This meant modifying multiple values from c# was slow.

    From Unity 5.4 on they added real array assignment functions, like
    material.SetFloatArray("_PropertyName", values);
    , but removed the (kind of hacky) individual assignment support.

    This might seem like a huge pain, especially if you just want to assign a single value without updating the entire array ... but that's probably not what was ever happening to begin with.

    In the pre 5.4 days, Unity was keeping track of the array for you. When you assigned a value it'd create an array of the appropriate size (based on the fixed size the shader defined) and you'd be assigning the individual values of the array. However Unity was most likely then uploading the entire array to the GPU at the start of rendering the next frame still.

    Post 5.4 the only real change is you're handling the management of the array yourself. For changing singular values this is probably slightly slower, as the entire array is now getting copied from c# to c++ before also getting uploaded to the GPU. But the amount of data isn't really the bottleneck for how slow this is, it's how many function calls need to happen to do it. And the amount of data Unity (or any real time renderer) is uploading to the GPU every frame is way, way more than most people realize. Your entire array of 250 floats is going to be a single digit percentage of the total amount of data being uploaded for a normal game scene.
     
    wannabee and lilacsky824 like this.
  3. wannabee

    wannabee

    Joined:
    Dec 18, 2012
    Posts:
    3
    bgolus, thank you so much the answer. Sad that you cannot do this (they could at least have left the hackish way). But good to know the state of affairs! Thank you.

    Do you have any knowledge about the current setting for Textures? There seem to be no SetTextureArray function...
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    10,900
    Correct. Because that’s not something GPUs could do until DirectX12.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    10,900
    To further explain, an "array of textures" is a relatively new thing for graphics APIs. To make proper use of them requires fairly significant reworking on a lot of the engine internals, and even some of the user facing aspects, like how materials work. Even if they did add support for arrays of textures, it wouldn't work on anything using DirectX 11 or OpenGL, and there's no fall back that can work on those older APIs / GPUs. It's a fundamentally different way of handling resources that's not backwards compatible. And Unity is still ultimately a DX11 and OpenGL based renderer that has some extra code to make it also run using DirectX 12, Vulkan, and Metal, but still mostly using the older style of resource management and rendering.

    The alternative that does work on modern and most older GPUs is a texture array. You might be reading that and thinking "but, isn't that the same thing?!" A texture array, or more specifically a Texture2DArray, is a single texture object that has multiple layers. Each layer has to be the exact same resolution and format. As far as the material & shader are concerned, it's a single texture. In the shader you sample it like any other texture, but using a texture array specific sample function that takes a third "layer index" parameter in addition to the UV.
     
  6. wannabee

    wannabee

    Joined:
    Dec 18, 2012
    Posts:
    3
    Thank you so much. Really, really helpful. I just got TextureArray working. Super nice!
     
unityunity