Search Unity

data compression of structuredBuffer floats into half/fixed data types?

Discussion in 'Shaders' started by drudiverse, Mar 9, 2016.

  1. drudiverse

    drudiverse

    Joined:
    May 16, 2013
    Posts:
    218
    is 2am and I may be mission something obvious... I have some 200mb structured buffers that i am passing to a shader, and i only need to use half or fixed data types inside the shader.

    What can I do to send fixed precision data into the graphics card, by packing a normal c# array?
     
    Last edited: Mar 9, 2016
  2. Michal_

    Michal_

    Joined:
    Jan 14, 2015
    Posts:
    365
    DirectX* or desktop OpenGL doesn't really support half or fixed data types. Every half or fixed variable you use will eventually be replaced by float. I think it is safe to say that desktop GPUs don't support them either. Lower precision variables are only relevant for mobile GPUs (OpenGL ES).

    * DirectX 11 actually supports low precision floats since Windows 8. But that's because Windows now run on mobile GPUs as well.
     
  3. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    DirectX and OpenGL actually do support it, but the drivers of all major GPU manufacturers simply turn everything into float. So, half becomes float, fixed becomes float and, yes, double also becomes float. It was done as an optimization, since these desktop GPU's are very fast in dealing with float's, but have (/had) no specific hardware to deal with half or double types.
     
  4. Michal_

    Michal_

    Joined:
    Jan 14, 2015
    Posts:
    365
    I think DirectX and OpenGL support them only for language compatibility. So I would say everything is float before it reaches the driver but that doesn't really matter...
     
  5. drudiverse

    drudiverse

    Joined:
    May 16, 2013
    Posts:
    218
    Is it possible to send an array in a compressed form in the graphics card, say if you pass an integer array to a shader where every integer is made of 10 figures and then you devide it into two 5 figure numbers, in UnityCG.cginc there are perhaps some encode and decode functions to turn one data type into another? in hlsl there are some packing rules to convert between 16 bit data and 4 bit data.

    In unity when i write a compute shader array i have to write: bufferPos = new ComputeBuffer (instanceCount, 12); which is the bit depth of the data, i didn't find any options if i can send 4 bit computeBuffers into the graphics memory and then they would be converted to 16 bit by the graphics when an array value is read to the vertex shaders?

    It's really confusing.
     
  6. Michal_

    Michal_

    Joined:
    Jan 14, 2015
    Posts:
    365
    That stride parameter in ComputeBuffer constructor is size of one element in bytes. Not bits. It has to match the size of one element as declared in compute shader. For example, consider this compute buffer declared in compute shader:
    Code (CSharp):
    1. struct MyElement
    2. {
    3.     float4 color;
    4.     int index;
    5. };
    6.  
    7. StructuredBuffer<MyElement> myElements;
    One element has 4 floats and one int. Both float and int has 4 bytes (everything in structured buffer must be 4-byte aligned). So size of MyElement is 5 * 4 bytes -> 20 bytes. And that is the stride you have to use when you declare ComputeBuffer in Unity:
    Code (CSharp):
    1. buffer = new ComputeBuffer (count, 20);
    Now to your other question. You can encode multiple numbers into one variable if you want. For example, you can store one short/half in lower 16 bits of one integer and second short/half in upper 16 bits of that same integer. But you have to encode/decode it yourself. I don't know if unity has any functions for that.
     
    mailey99 likes this.
  7. Quatum1000

    Quatum1000

    Joined:
    Oct 5, 2014
    Posts:
    889
    Every desktop GPU supports half and fix register operation since the last millennium. 16-bit floating point formats have existed including that of Hitachi's HD61810 DSP[1] of 1982, Scott's WIF[2] and the 3dfx Voodoo Graphics processor.[3].
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    So, apart from this being a thread necro ... desktop GPUs removed all support for 16 bit math sometime around 14 years ago. It remains in some fixed function hardware, like for vertex data or texture formats, but otherwise does not exist. @Michal_ and @jvo3dc were correct there.

    In the context of this specific thread's topic, if you wanted to pass a 16 bit float to the GPU via a structured buffer, you'd need to use a
    uint
    with the bits of the 16 bit float packed into either the first or second half of it, and then decode it from a 16 bit float to a 32 bit float in the shader using the
    f16tof32()
    intrinsic.

    fixed
    and
    half
    both define a minimum precision, but have no maximum, and since a 32 bit float meets the minimum requirements that's what gets used. Some modern desktop GPUs have since added true 16 bit support back in which can be used with
    min16float
    types, but it's not guaranteed so may still be a 32 bit float, and the
    half
    type is still always a 32 bit float. Since a 16 bit precision isn't guaranteed, and Unity doesn't have a way to check if the current desktop GPU supports 16 bit floats, it'd be unsafe to use
    min16float
    in a structured buffer as you don't know if the shader will evaluate that as a 16 bit or 32 bit float.
     
    Quatum1000 and UnityMaru like this.
  9. Quatum1000

    Quatum1000

    Joined:
    Oct 5, 2014
    Posts:
    889
    Why then OpenGL Inc decide to add support for hfloat in 2004. And at this time the GPU vendors removed their hfloat support at the same time? But for sure at this time a lot of curiosities happen to the GPU vendors on the trip to be the first in every row.

    What I do not understand is, why Unity advertising half / fixed as performance and power on their documentation site for shader code. Where is the performance advantage of using half or fixed if they use 32bitfloat anyway?
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Support had been added to some desktop GPUs (GeForce FX for example) just before it being added officially to OpenGL. And it remained in some professional GPUs even after being removed from consumer GPUs. If you read the official 16 bit data type extension, ARB_half_float_pixel, it's based off of the SGI implementation for their own hardware. Nvidia actually had their own extension to enable it on their GPUs.

    As for Unity's push of it, half precision has always existed on mobile in the form of OpenGLES
    mediump
    . Unity was predominantly a mobile focused engine for the first several versions of its existence, and remains the platform it is most used on. Some older GPUs would run everything at 16 bit precision unless explicitly told to run in
    highp
    . On mobile gpus that still support half precision math it is a massive performance improvement to use it over single precision.

    On desktop consumer GPUs, it can be a big performance improvement, but on others it's no faster or even slower than full precision. But ALU (raw math) performance is rarely the major bottleneck it is on mobile.
     
    Quatum1000 likes this.
  11. Quatum1000

    Quatum1000

    Joined:
    Oct 5, 2014
    Posts:
    889
    This is like the Heisenberg's uncertainty principle, can be - or can be not. :)
     
  12. xujinm

    xujinm

    Joined:
    Dec 11, 2018
    Posts:
    13
    Does anyone know why half4 don't work with ComputeShader in Metal?but float4 works well
     
  13. Quatum1000

    Quatum1000

    Joined:
    Oct 5, 2014
    Posts:
    889
    Sorry for the long time to reply. Did you have a simple shader code example that is complete?
    Perhaps you have solved it already.
     
  14. xujinm

    xujinm

    Joined:
    Dec 11, 2018
    Posts:
    13
    Like this:

    Code (CSharp):
    1. //float4 myColor;
    2. half4 myColor;
    3. [numthreads(16,2,1)]
    4. void CSMain(uint3 id : SV_DispatchThreadID)
    5. {
    6. float4 output = myColor
    7.     _VolumeInject[id] = output;
    8.  
    9. }
    10.  
    11. cmd.SetGlobalColor("myColor",Vector4.one)
    when I declared myColor is half4,this output will display green,but when I declared myColor is float4,it will be white.
    Thank you for your reply:)
     
    Quatum1000 likes this.