Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Update a meshFilter mesh directly into GPU memory

Discussion in 'General Graphics' started by methusalah999, Sep 29, 2020.

  1. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    637
    Hi there,

    I'm using a compute buffer to change the vertices of a mesh. Then, I read this vertex data back into CPU code via
    myComputeBuffer.GetData()
    and update the mesh vertices with
    myMeshFilter.mesh.SetVertices()
    .

    This is working pretty well but the GetData method is a huge performance bottleneck in my situation: moving data back to the CPU is slow by design, and the GetData method have to wait for the GPU to complete all the pending dispatched kernels. In addition, the MeshFilter will have to send the updated mesh to the GPU again.

    One solution would be to use the buffer into a rendering shader and move the vertices in place in the vertex method. This is what I do in HairStudio but that won't work here, because I need the mesh to be renderable with any surface material.

    It would be much faster if I could update the mesh that is stored on the GPU by the MeshFilter, directly from my compute shader (or by setting a compute buffer). Is there any way to do that?
     
    Last edited: Oct 5, 2020
    voxelltech likes this.
  2. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    637
    Thanks to GetNativeBufferPtr methods, I can get the GPU memory pointer for the vertices of a mesh, and the GPU pointer of a compte buffer. But that alone does not solve my issue.

    I would need one of the following:
    - assign a GPU memory pointer for mesh vertex buffer or a compute buffer,
    - copy data from a native buffer to the other without locking the CPU code,
    - write on a buffer using a native buffer pointer from the compute shader directly.

    Is one of these solution possible in the actual API? Any other solution? (@bgolus help!)
     
  3. blossomgames

    blossomgames

    Joined:
    Oct 23, 2013
    Posts:
    30
    One way to do this, although probably work-consuming and error-prone, would be to use GetNativeBufferPtr but *in a plugin*, where you have access to Vulkan/D3D11/D3D12/OGL/Metal APIs. But that is a last resort imho, given the complexity.
     
  4. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    637
    Thank you for the idea. Do you think there is a way to copy data from a GPU buffer to another in such a plugin, without actually reading that data in the CPU code? If it is only a matter of calling such a function, with the pointers as arguments, it may be no big deal at the end of the day.

    Of course, it would be wonderfull to have the feature in the Unity API.

    In the meantime, I've implemented the feature inside a custom shader, and the performance difference is an order of magnitude (100 to 1000fps). But I really need this tool to be useable with any shader.
     
  5. Meetem

    Meetem

    Joined:
    Feb 10, 2014
    Posts:
    18
    @methusalah, yes, there's a way for sure. Even without native plugin you can copy data via Compute Shader. You need to define structure for your mesh (you can check it up with RenderDoc or unity mesh preview and then use it as StructuredBuffer/RWStructuredBuffer. [pos(float3), normals(float3), uv1(float4), uv2(float4)..] is common layout. But be careful, I think unity can change the layout when mesh is changed by CPU with SetNormals/SetVertices e.t.c). I can't say for sure, but there should also be indices buffer, AFAIK unity using separate buffers for indices/vertices.

    You can also use DX11's CopyResource, ComputeBuffer and Mesh Data is basically just a buffers.

    Even more, you can try write directly to mesh buffer! This would save you 1 copy operation and memory bandwidth
     
    Last edited: Oct 10, 2020
  6. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    637
    Ok but how do you pass the mesh vertex buffer into your compute shader? AFAIK you can only pass compute buffers and not a pointer.

    Can you give me details about the implementation?
     
  7. Meetem

    Meetem

    Joined:
    Feb 10, 2014
    Posts:
    18
    methusalah999 oh, I've missed you can't do that without native plugin and DX bind. But can you draw your mesh with DrawMeshProcedural straight from ComputeBuffer? May be this would help

    In case you'll go for DX11 native plugin there's CopyResource function for copy two ID3D11Buffer* (which inherits ID3D11Resource*). And CSSetShaderResources for compute shader, you'll need to declare buffer register explicitly like StructureBuffer<Struct> _MyMesh : register(u1);
     
    Last edited: Oct 10, 2020
    methusalah999 likes this.
  8. blossomgames

    blossomgames

    Joined:
    Oct 23, 2013
    Posts:
    30
    As @Meetem mentioned, you'd use CopyResource function to copy one GPU buffer to the other. Although to be honest I am not sure if this will work that easily with DX11 buffers as when they are created, different sets of flags are set for various buffers (vertex, index, structured, etc.) and I am not sure how compatible they are with each other.

    Anyway, if you decided to give plugins a try here (https://github.com/Unity-Technologies/NativeRenderingPlugin) is a core Unity sample on how to implement custom rendering plugin. You should be able to at least easily experiment with it.
     
  9. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    637
    Thanks. It seems that I will have to learn and experiment a lot before ending up with the simplest plugin ever ^^