Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Is it possible to run matrix transformations on the GPU?

Discussion in 'Scripting' started by kiriri, Jan 25, 2015.

  1. kiriri

    kiriri

    Joined:
    Jan 14, 2011
    Posts:
    107
    Hi,
    I've recently written a physics engine that calculates static collision tests between an elipsoidal column and a skinned mesh (every frame). It's essentially O(n) where the total calculation time is pretty much vertexCount * TimeOf(Matrix4x3 * Vector3). Of course I have separated the skinned mesh into individual parts and bounds-checked them first.
    I need hundreds of colliders and my mesh is a high res character.

    Having said that, I did everything I could think of but it's still not enough.
    I've heard that gpus can calculate matrix transformations within a fraction of the time the cpu needs via parallel processing. So I was wondering, are there ways to access GPU functionalities in Unity directly? I know compute shaders can do what I want, but they are restricted to Windows so that's no good.

    Should I perhaps add a material to my colliders and use their shaders to calculate it for me? If so, are there any guides on how to do this most efficiently? Are RenderTextures the way to go then?
    Or should I instead look for 3rd party libraries like https://code.google.com/p/gpuocelot/ ? To be honest, they all sound a bit bugprone to me and I don't relish the thought of not being able to use the gpu in webapplications, so I haven't actually given them a try yet.
    Or should I instead write my own library ? (dreadful thought, I know, but I really only need this one functionality so it might be feasable)

    Any help would be appreciated, I don't want to spend a month studying gpu drivers just to find out that it could've been so much easier if only I chose a different approach.

    Thanks,
    kiriri
     
    Last edited: Jan 25, 2015
  2. P-O-M

    P-O-M

    Joined:
    Mar 2, 2013
    Posts:
    8
    Did you find a way to do matrix manipulations on GPU?
    I have massive matrices in my projects that i need to multiply, I'm sure a GPU will do it much faster.

    Roy.
     
  3. kiriri

    kiriri

    Joined:
    Jan 14, 2011
    Posts:
    107
    Things changed since that post. Newer versions of OpenGL(4.2+) and OpenGL ES (3.1+) support compute shaders. OpenGL ES is what you would use on mobile devices.
    Now here's the answer I would have given 2 years ago, which is still valid for people who target older mobile devices :

    Use the Geometry Shader pipeline. This is how general-purpose gpu programs were written prior to cuda/opencl/vulkan.
    It's undocumented in Unity so I'm not surprised nobody knows about it though.
    In Unity's case you would first write a shader with vert, frag and geometry functions. The geometry function will return max 1 vertex (it will never create any real vertex, but shaders can't declare that directly) and do whatever calculations you need. It will use a buffer for in/output which you can set via c#/js.
    Here's an example for a geometry shader that reads from a buffer based on its "vertex id".

    Code (CSharp):
    1. StructuredBuffer<float2> pointBuffer : register(u1);
    2.  
    3.     struct vs_out {
    4.         float4 pos : SV_POSITION;
    5.     };
    6.  
    7.     vs_out vert (uint id : SV_VertexID)
    8.     {
    9.         vs_out o;
    10.         o.pos = float4(pointBuffer[id], 0, 1);
    11.         return o;
    12.     }
    13.  
    14.     struct gs_out {
    15.         float4 pos : SV_POSITION;
    16.         float2 uv : TEXCOORD0;
    17.     };
    18. [maxvertexcount(1)]
    19.     void geom (point vs_out input[1], inout TriangleStream<gs_out> outStream)
    20.     {
    21.         input[0].pos ... [B]Do your stuff with it[/B]
    22.     }
    After you create that shader, make it a material and assign it to your cpu monobehaviour. In the MB you can use it like this :

    Code (CSharp):
    1. mat.SetBuffer("pointBuffer", pointBuffer);
    2. ComputeBuffer.CopyCount(subunitPositionsBuffer, cbDrawArgs, 0);
    3. mat.SetPass(0);
    4. Graphics.DrawProceduralIndirect(MeshTopology.Points, cbDrawArgs, 0);
    The buffers need to be setup properly though. I'm not exactly sure what the values in the cbDrawArgs mean exactly, but the first one seems to be the vertex count (the amount of times the geometry shader is called).
    The next 32 bit variables seem to be related to color , but it's of no relevance to us.
    This is how I create my cbDrawArgs buffer :

    Code (CSharp):
    1.         if (cbDrawArgs == null)
    2.         {
    3.             cbDrawArgs = new ComputeBuffer(1, 16, ComputeBufferType.IndirectArguments);
    4.             var args = new int[4];
    5.             args[0] = points.Length; // gemetry shader count (how often to execute the geometry shader)
    6.             args[1] = 1; // color multiplier?
    7.             args[2] = 0;
    8.             args[3] = 0;
    9.             cbDrawArgs.SetData(args);
    10.         }
    The actual point buffer is straight forward :

    Code (CSharp):
    1.         if (pointBuffer == null)
    2.         {
    3.             pointBuffer= new ComputeBuffer(points.Length, sizeof(float) * 2, ComputeBufferType.Default);
    4.             subunitPositionsBuffer.SetData(points);
    5.         }
    And that's it, this is how you create a gpu script that can manage itself without any data exchange between the cpu and gpu. So if your gpu scripts change the amount of "points" (or whatever you have in your case) that make it to the next iteration or if you have multiple scripts in a sequence, you will never have to send your data to the cpu and count them. Instead you can just pass you cbDrawArgs around.

    If this seems overly complicated to you, there is also the Graphics.DrawProcedural(MeshTopology.Points, int::vertexCount ...) function, which will let you skip the DrawArgs compute buffer.

    PS: I did not check if the geometry shader works. May be some errors, but they should be self explanatory.
    PPS: WebGL does not support Compute Shaders so if you're developing a webapplication you will need to go for the geometry shader.
     
    Last edited: Aug 3, 2016
    Develax likes this.
  4. P-O-M

    P-O-M

    Joined:
    Mar 2, 2013
    Posts:
    8
    That is a great answer thanks :)

    Roy.