Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question Problems with Advanced Mesh API to optimize Vertex Attributes

Discussion in 'General Graphics' started by MarcoElz, Feb 1, 2022.

  1. MarcoElz

    MarcoElz

    Joined:
    Jan 28, 2017
    Posts:
    17
    The idea is to optimize some meshes data at runtime using the Advanced Mesh API. But I'm having issues after I try it. The Mesh ends up distorted, and I'm getting an error:

    SkinnedMeshRenderer: Mesh has been changed to one which is not compatible with the expected mesh data size and vertex stride. Aborting rendering

    This is what I'm doing:
    1. Clone the original mesh
    2. Get its VertexAttributes by using GetVertexAttributes()
    3. Loop thru each existing attribute
    4. If it is Position, Normal, Tangent, or BlendWeight, I set its format to Float16 and the dimension to 4.
    5. If it is TexCoord0, I set its format to Float16 and dimension to 2
    6. If it is BlendIndices, I change its format to UInt16
    7. Set mesh data using mesh.SetVertexBufferParams()
    8. Set the mesh to the Skinned Mesh Renderer.

    If I remove the code that modifies Position, Normal and Tangent Attributes, I don't get the distortion, and the other Attributes get optimized. So, I think that I'm doing something wrong with those...

    The mesh looks like it was successfully optimized.
    upload_2022-2-1_10-19-12.png

    upload_2022-2-1_10-18-44.png

    But in the scene, I have that error I wrote at the beginning, and the mesh is distorted:
    upload_2022-2-1_10-21-35.png



    And, here is the code:
    Code (CSharp):
    1. private Mesh Optimize(Mesh original)
    2.         {
    3.             var mesh = Instantiate(original);
    4.                 var attributes = mesh.GetVertexAttributes();
    5.                 for (int i = 0; i < attributes.Length; i++) {
    6.                     var attribute = attributes[i];
    7.  
    8.                     if (attribute.attribute == VertexAttribute.Position
    9.                         || attribute.attribute == VertexAttribute.Normal
    10.                         || attribute.attribute == VertexAttribute.Tangent
    11.                         || attribute.attribute == VertexAttribute.BlendWeight
    12.                         )
    13.                     {
    14.                         attribute.format = VertexAttributeFormat.Float16;
    15.                         attribute.dimension = 4;
    16.                     }
    17.  
    18.                    
    19.                     if (attribute.attribute == VertexAttribute.TexCoord0) {
    20.                         attribute.format = VertexAttributeFormat.Float16;
    21.                         attribute.dimension = 2;
    22.                     }
    23.  
    24.                    
    25.                     if (attribute.attribute == VertexAttribute.BlendIndices) {
    26.                         attribute.format = VertexAttributeFormat.UInt16;
    27.                     }
    28.                    
    29.                     //Save modified attribute
    30.                     attributes[i] = attribute;
    31.                 }
    32.                
    33.                 mesh.SetVertexBufferParams(mesh.vertexCount, attributes);
    34.                
    35.                 return mesh;
    36.         }
    Btw, I also tried using the mesh.SetVertexBufferData(), but it usually doesn't do anything else, and sometimes I get more errors with it.

    Thanks for reading all these!
     
  2. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    737
    @MartinTilo
    @richardkettlewell

    Y'all are always really great at helping out, and I appreciate it. This problem has been an issue we have been trying to solve off and on for a couple months now. Can you help or do you know who can?
     
  3. mabulous

    mabulous

    Joined:
    Jan 4, 2013
    Posts:
    198
    Just changing the VertexAttributeDescriptors but not changing the underlying vertex data to the format you're specifying will cause the GL to interpret the old vertex data with your new (and mismatching) descriptor.

    you'll have to define your new vertex format

    Code (CSharp):
    1.  
    2.     [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    3.     public struct CustomVertex
    4.     {
    5.       public Half posX, posY, posZ, posW;
    6.       public Half normX, normY, normZ, normW;
    7.       public Half tanX, tanY, tanZ, tanW;
    8.       public Half u, v;
    9.     };
    Half needs to be a struct that implements An IEEE-754 16bit half float value https://www.khronos.org/opengl/wiki/Small_Float_Formats (.Net 5 has native support for them as System.Half, but there are open source implementations available)

    Then you need to create an array of them and set each attribute to the correct value and finally set this array using https://docs.unity3d.com/ScriptReference/Mesh.SetVertexBufferData.html
     
    Last edited: Feb 2, 2022
  4. MarcoElz

    MarcoElz

    Joined:
    Jan 28, 2017
    Posts:
    17
    Thank you so much for your help and time @mabulous .

    So, I tried a few things but I couldn't make it work...
    First, I tried with Unity.Mathematics half type. But it didn't work. So, I searched for an open source Half, (we are using Unity 2020.3.19f, which doesn't have .Net 5), which didn't work either.

    What I did was to make the struct as you said, but I made three because the Stream for Position, Normal and Tanget is 0, the TexCoord0 is 1, and the BlendWeight and BlendIndices is 2. (I get this from the Vertex Attributes)


    Code (CSharp):
    1. [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    2.         public struct VertexData0 {
    3.                 public Half posX, posY, posZ, posW;
    4.                 public Half normX, normY, normZ, normW;
    5.                 public Half tanX, tanY, tanZ, tanW;
    6.  
    7.                 public VertexData0(Mesh mesh, int index) {
    8.                     var vertex = mesh.vertices[index];
    9.                     posX = HalfHelper.SingleToHalf(vertex.x);
    10.                     posY = HalfHelper.SingleToHalf(vertex.y);
    11.                     posZ = HalfHelper.SingleToHalf(vertex.z);
    12.                     posW = HalfHelper.SingleToHalf(0f);
    13.  
    14.                     var normal = mesh.normals[index];
    15.                     normX = HalfHelper.SingleToHalf(normal.x);
    16.                     normY = HalfHelper.SingleToHalf(normal.y);
    17.                     normZ = HalfHelper.SingleToHalf(normal.z);
    18.                     normW = HalfHelper.SingleToHalf(0f);
    19.                  
    20.                     var tangent = mesh.tangents[index];
    21.                     tanX = HalfHelper.SingleToHalf(tangent.x);
    22.                     tanY = HalfHelper.SingleToHalf(tangent.y);
    23.                     tanZ = HalfHelper.SingleToHalf(tangent.z);
    24.                     tanW = HalfHelper.SingleToHalf(tangent.w);
    25.                 }
    26.             }

    Code (CSharp):
    1. [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    2.             public struct VertexData1 {
    3.                 public Half uvX, uvY;
    4.  
    5.                 public VertexData1(Mesh mesh, int index) {
    6.                     uvX = HalfHelper.SingleToHalf(mesh.uv[index].x);
    7.                     uvY = HalfHelper.SingleToHalf(mesh.uv[index].y);
    8.                 }
    9.             }

    Code (CSharp):
    1. [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    2.             public struct VertexData2 {
    3.                 public Half blendWeightX, blendWeightY, blendWeightZ, blendWeightW;
    4.                 public ushort blendIndex0, blendIndex1, blendIndex2, blendIndex3;
    5.  
    6.                 public VertexData2(Mesh mesh, int index) {
    7.  
    8.                     var boneData = mesh.boneWeights[index];
    9.                     blendWeightX = HalfHelper.SingleToHalf(boneData.weight0);
    10.                     blendWeightY = HalfHelper.SingleToHalf(boneData.weight1);
    11.                     blendWeightZ = HalfHelper.SingleToHalf(boneData.weight2);
    12.                     blendWeightW = HalfHelper.SingleToHalf(boneData.weight3);
    13.                  
    14.                     blendIndex0 =  (ushort) boneData.boneIndex0;
    15.                     blendIndex1 =  (ushort) boneData.boneIndex1;
    16.                     blendIndex2 =  (ushort) boneData.boneIndex2;
    17.                     blendIndex3 =  (ushort) boneData.boneIndex3;
    18.                 }
    19.             }
    And using them like this after mesh.SetVertexBufferParams:
    Code (CSharp):
    1.                 var vertices0 = new VertexData0[mesh.vertices.Length];
    2.                 for (int i = 0; i < vertices0.Length; i++) {
    3.                     vertices0[i] = new VertexData0(mesh, i);
    4.                 }
    5.                 mesh.SetVertexBufferData(vertices0, 0, 0, vertices0.Length, 0);
    6.                
    7.                
    8.                 var vertices1 = new VertexData1[mesh.vertices.Length];
    9.                 for (int i = 0; i < vertices1.Length; i++) {
    10.                     vertices1[i] = new VertexData1(mesh, i);
    11.                 }
    12.                 mesh.SetVertexBufferData(vertices1, 0, 0, vertices1.Length, 1);
    13.                
    14.                
    15.                  var vertices2 = new VertexData2[mesh.vertices.Length];
    16.                  for (int i = 0; i < vertices2.Length; i++) {
    17.                      vertices2[i] = new VertexData2(mesh, i);
    18.                  }
    19.                 mesh.SetVertexBufferData(vertices2, 0, 0, vertices2.Length, 2);
    But I'm getting the exactly same results as my first post. The same deformation and the same error:
    SkinnedMeshRenderer: Mesh has been changed to one which is not compatibile with the expected mesh data size and vertex stride. Aborting rendering.

    Again, thank you for all your help. I would like to know what I'm doing wrong.
     
  5. mabulous

    mabulous

    Joined:
    Jan 4, 2013
    Posts:
    198
    First: cool, I wasn't aware unity had a half float implementation in its public namespace - I'm sure this one should work fine.

    Second: if you set your W component of the position to 0.0 instead of 1.0 and u write custom shaders, make sure they read position as float3 and not as float4 (otherwise it would be a homogeneous direction rather than a homogeneous position). With default shaders this should be no issue though.

    then, rather than relying on your code that 'patches' the current vertexdescriptors, rather define them as a completely new layout, so that you can be sure it matches your custom vertex attributes (mainly the correct order is not ensured in your current implementation).

    Otherwise your code looks good, so I think your patched vertex description doesn't fully match your data, so better create a new one instead of patching the existing one.

    Not fully sure whether it's required, but you might also need to add the following after setting everything else:

    Code (CSharp):
    1.         SubMeshDescriptor mainMeshDescriptor = new SubMeshDescriptor();
    2.         mainMeshDescriptor.baseVertex = 0;
    3.         mainMeshDescriptor.bounds = meshBounds;
    4.         mainMeshDescriptor.firstVertex = 0;
    5.         mainMeshDescriptor.vertexCount = vertexCount;
    6.         mainMeshDescriptor.indexStart = 0;
    7.         mainMeshDescriptor.indexCount = indexCount;
    8.         mainMeshDescriptor.topology = MeshTopology.Triangles;
    9.         mesh.SetSubMesh(0, mainMeshDescriptor, MeshUpdateFlags.DontRecalculateBounds);
    10.         mesh.UploadMeshData(false);
     
    Last edited: Feb 3, 2022
  6. mabulous

    mabulous

    Joined:
    Jan 4, 2013
    Posts:
    198
    On a separate note, for your normal and tangent component, you should use VertexAttributeFormat.SNorm16 instead of Float16, since you'll get more precision in the [-1,1] range at the same component size than you'll get from Float16 (on your custom vertex side, make sure your normal and tangent vectors are normalized, then convert the float components to short by multiplying with 32767 and then casting to short).

    For the UV components, if all your UV coordinates are constrained to the [0,1]x[0,1] square, you'd better use VertexAttributeFormat.UNorm16 instead of Float16 for the same reason (multiply your float UVs by 65535 and cast to ushort)

    Blendweights afaik also are always between 0.0 and 1.0, so I bet you'll get away without problems with a VertexAttributeFormat.UNorm8 even (just make sure when converting that the original float values really are clamped to [0.0,1.0], otherwise weird things will happen)

    And since your mesh has less than 256 bones, for the bone indices you should be able to use UInt8 instead of UInt16
     
    Last edited: Feb 3, 2022
  7. MarcoElz

    MarcoElz

    Joined:
    Jan 28, 2017
    Posts:
    17
    Again, thank you so much for your time, @mabulous; I appreciate it.

    So, I tried what you suggested, and while it improves, there are still problems.
    I'm using a simple layout first to get it to work. I can experiment with different formats later.

    First, I created an empty mesh and set the VertexAttributes.

    Code (CSharp):
    1.             //Create Mesh base
    2.             var mesh = new Mesh();
    3.             mesh.subMeshCount = 1;
    4.          
    5.          
    6.             //Set VertexAttribute Layout
    7.             var layout = new[]
    8.             {
    9.                 new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float16, 4, 0),
    10.                 new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float16, 4, 0),
    11.                 new VertexAttributeDescriptor(VertexAttribute.Tangent, VertexAttributeFormat.Float16, 4, 0),
    12.                 new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float16, 2, 1),
    13.                 new VertexAttributeDescriptor(VertexAttribute.BlendWeight, VertexAttributeFormat.Float16, 4, 2),
    14.                 new VertexAttributeDescriptor(VertexAttribute.BlendIndices, VertexAttributeFormat.UInt16, 4, 2),
    15.             };
    16.          
    17.             mesh.SetVertexBufferParams(original.vertexCount, layout);
    18.  
    I set the Buffer Data exactly as before.

    If I stop here. I get this result:
    upload_2022-2-3_19-37-41.png

    There is no preview, no triangles, and no bones.
    So, next, I use the SetIndexBufferParams and SetIndexBufferData.

    Code (CSharp):
    1.             mesh.SetIndexBufferParams((int) original.GetIndexCount(0), IndexFormat.UInt16);
    2.          
    3.             var indexBuffer = new NativeArray<ushort>((int) original.GetIndexCount(0), Allocator.Temp);
    4.             for (var i = 0; i < original.GetIndexCount(0); i++)
    5.                 indexBuffer[i] = (ushort) original.GetIndices(0)[i];
    6.          
    7.             mesh.SetIndexBufferData(indexBuffer, 0, 0, (int) original.GetIndexCount(0));
    And, I have to also set the SubMeshDescriptor:

    Code (CSharp):
    1.             //Create submesh
    2.             var originalSubMesh = original.GetSubMesh(0);
    3.             SubMeshDescriptor mainMeshDescriptor = new SubMeshDescriptor();
    4.             mainMeshDescriptor.baseVertex = originalSubMesh.baseVertex;
    5.             mainMeshDescriptor.bounds = originalSubMesh.bounds;
    6.             mainMeshDescriptor.firstVertex = originalSubMesh.firstVertex;
    7.             mainMeshDescriptor.vertexCount = originalSubMesh.vertexCount;
    8.             mainMeshDescriptor.indexStart = originalSubMesh.indexStart;
    9.             mainMeshDescriptor.indexCount = originalSubMesh.indexCount;
    10.             mainMeshDescriptor.topology = MeshTopology.Triangles;
    11.             mesh.SetSubMesh(0, mainMeshDescriptor, MeshUpdateFlags.DontRecalculateBounds);
    12.             mesh.UploadMeshData(true);
    And I got this result:
    upload_2022-2-3_19-44-23.png
    No preview, but triangles are ok. But sadly, no bones.
    And surprisingly, Position, Normal, and Tangent are Float32 instead of Float16.

    Having no bones gives me this result too:
    upload_2022-2-3_19-45-19.png
    The model is rotated. And that error on the inspector that says that it is missing the bones.


    So, why does the Position, Normal, and Tangent are Float32... Well, after some test, it seems that removing this line fixes it:
     mesh.UploadMeshData(false);


    upload_2022-2-3_19-48-9.png
    Still, the bones problem is there. But we get back the error:
    SkinnedMeshRenderer: Mesh has been changed to one which is not compatible with the expected mesh data size and vertex stride. Aborting rendering.


    Really weird...
    Thank you so much for your time.
     
  8. mabulous

    mabulous

    Joined:
    Jan 4, 2013
    Posts:
    198
    I just checked some of my code, and here's the code which does something very similar as you are doing (I didn't deal with bones though) and which works for me (note that I'm not creating a new mesh but instead I mesh.Clear() the original mesh. Not sure whether that makes a difference)


    Code (CSharp):
    1.  
    2.       mesh.Clear();
    3.       VertexAttributeDescriptor[] layout = new[]
    4.       {
    5.         new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.SNorm16, 2, 0),
    6.         new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.UInt8, 4, 0),
    7.         new VertexAttributeDescriptor(VertexAttribute.TexCoord1, VertexAttributeFormat.UNorm16, 2, 1)
    8.       };
    9.       mesh.SetIndexBufferParams(indices.Length, UnityEngine.Rendering.IndexFormat.UInt32);  // <-- I don't recall why, but I think for some reasons I had to do this before setting the vertex buffer params rather than after it.
    10.  
    11.       mesh.SetVertexBufferParams(compressedVertices.Count, layout);
    12.       mesh.SetVertexBufferData(compressedVertices.ToArray(), 0, 0, compressedVertices.Count, 0, MeshUpdateFlags.DontValidateIndices);
    13.       mesh.SetVertexBufferData(compressedUVs.ToArray(), 0, 0, compressedUVs.Count, 1, MeshUpdateFlags.DontValidateIndices);
    14.  
    15.       // Set proper index format based on number of vertices added.
    16.       if (compressedVertices.Count > 65535)
    17.       {
    18.         mesh.SetIndexBufferData(indices, 0, 0, indices.Length, MeshUpdateFlags.DontValidateIndices);
    19.       }
    20.       else
    21.       {
    22.         mesh.SetIndexBufferParams(indices.Length, UnityEngine.Rendering.IndexFormat.UInt16);
    23.         ushort[] indices16 = new ushort[indices.Length];
    24.         for (int i = 0; i < indices.Length; i++)
    25.         {
    26.           indices16[i] = (ushort)indices[i];
    27.         }
    28.         mesh.SetIndexBufferData(indices16, 0, 0, indices.Length, MeshUpdateFlags.DontValidateIndices);
    29.       }
    30.  
    31.       mesh.subMeshCount = 1;
    32.       SubMeshDescriptor subMeshDescriptor = new SubMeshDescriptor();
    33.       subMeshDescriptor.baseVertex = 0;
    34.       subMeshDescriptor.bounds = meshBounds;
    35.       subMeshDescriptor.indexCount = indices.Length;
    36.       subMeshDescriptor.indexStart = 0;
    37.       subMeshDescriptor.topology = MeshTopology.Triangles;
    38.  
    39.       mesh.SetSubMesh(0, subMeshDescriptor, MeshUpdateFlags.DontRecalculateBounds);
    40.  
    41.       mesh.name = mesh.name + "_compressed";
    42.       mesh.bounds = meshBounds;
    43.       mesh.UploadMeshData(false);
     
  9. MarcoElz

    MarcoElz

    Joined:
    Jan 28, 2017
    Posts:
    17
    Thanks again!
    I tried a few things based on your code, but the bones still aren't correctly set.

    Hopefully, someone else can give us some inside on this issue.
     
  10. mabulous

    mabulous

    Joined:
    Jan 4, 2013
    Posts:
    198
  11. MarcoElz

    MarcoElz

    Joined:
    Jan 28, 2017
    Posts:
    17
    Thank you once again!! :)

    I tried what you suggested, compressing only position, normal, tanget and uv, and leaving bone data on original format. But the result is exactly the same. As if the bone data is not correctly set.

    I'll check that thread. Thanks!!
     
  12. AlexisDelforges

    AlexisDelforges

    Joined:
    Nov 30, 2021
    Posts:
    22
    Been working on this too, thanks for sharing ideas and code.

    Code (CSharp):
    1.  
    2.  
    3. #region MeshFormatUtils
    4.    
    5.     const MeshUpdateFlags DontUpdate = MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontResetBoneBounds | MeshUpdateFlags.DontValidateIndices;
    6.     const MeshUpdateFlags DontUpdateOrNotify = DontUpdate | MeshUpdateFlags.DontNotifyMeshUsers;
    7.     public static int GetIndexCount(Mesh.MeshData data)
    8.     {
    9.         int count = 0;
    10.         for (int i = 0; i < data.subMeshCount; i++)
    11.             count += data.GetSubMesh(i).indexCount;
    12.         return count;
    13.     }
    14.  
    15.     public static void CopyMeshData(Mesh.MeshDataArray sourceArray, Mesh.MeshDataArray destinationArray, VertexAttributeDescriptor[] attributes)
    16.     {
    17.         for (int i = 0; i < sourceArray.Length; i++)
    18.         {
    19.             Mesh.MeshData source = sourceArray[i];
    20.             Mesh.MeshData output = destinationArray[i];
    21.  
    22.             VertexAttributeDescriptor[] newAttributes = new VertexAttributeDescriptor[attributes.Length];
    23.  
    24.             for (int index = 0; index < attributes.Length; index++) {
    25.                 var attribute = attributes[index];
    26.                 VertexAttributeDescriptor vertexAttributeDescriptor = attribute;
    27.                 //Debug.Log (vertexAttributeDescriptor.dimension);
    28.                 if (vertexAttributeDescriptor.attribute == VertexAttribute.Position || vertexAttributeDescriptor.attribute == VertexAttribute.Normal) {
    29.                     vertexAttributeDescriptor.format = VertexAttributeFormat.Float16;
    30.                     vertexAttributeDescriptor.dimension = 4;
    31.                 }
    32.                 newAttributes[index] = vertexAttributeDescriptor;
    33.             }
    34.  
    35.             output.SetVertexBufferParams(source.vertexCount, newAttributes);
    36.             output.SetIndexBufferParams(GetIndexCount(source), source.indexFormat);
    37.  
    38.             for (int s = 0; s < source.vertexBufferCount; s++) {
    39.                 /*NativeArray<float3> vertices = source.GetVertexData<float3> (s);
    40.                 output.GetVertexData<byte> (s).CopyFrom (vertices.Reinterpret<byte> (12));*/
    41.                
    42.                 NativeArray<float3> vertices = source.GetVertexData<float3> (s);
    43.                 NativeArray<half4> compressedVertices = GetCompressedVertices (vertices);
    44.  
    45.                 //Debug.Log ("expected " + output.GetVertexData<byte>(s).Length);
    46.                 //Debug.Log ("actual " +compressedVertices.Reinterpret<byte>(8).Length);
    47.                 output.GetVertexData<byte>(s).CopyFrom(compressedVertices.Reinterpret<byte>(8));
    48.             }
    49.                
    50.             output.GetIndexData<byte>().CopyFrom(source.GetIndexData<byte>());
    51.             output.subMeshCount = source.subMeshCount;
    52.             for (int m = 0; m < source.subMeshCount; m++)
    53.                 output.SetSubMesh(m, source.GetSubMesh(m), DontUpdateOrNotify);
    54.         }
    55.     }
    56.  
    57.     private static NativeArray<half4> GetCompressedVertices (NativeArray<float3> vertices) {
    58.         NativeArray<half4> tmp = new NativeArray<half4> (vertices.Length, Allocator.Temp);
    59.         for (int index = 0; index < vertices.Length; index++) {
    60.             tmp[index] = ToHalf4(vertices[index]);
    61.         }
    62.         return tmp;
    63.     }
    64.  
    65.     public static void CopyTo(Mesh source, Mesh destination) {
    66.         using var sourceArray = Mesh.AcquireReadOnlyMeshData(source);
    67.         Mesh.MeshDataArray destinationArray = Mesh.AllocateWritableMeshData(1);
    68.         CopyMeshData(sourceArray, destinationArray, source.GetVertexAttributes());
    69.         Mesh.ApplyAndDisposeWritableMeshData(destinationArray, destination, DontUpdate);
    70.     }
    71.  
    72.     public static half4 ToHalf4 (float3 h) {
    73.         return new half4 ((half) h.x, (half) h.y, (half) h.z, half.zero);
    74.     }
    75. #endregion
    ndlr; this code is higly unoptimized, especially memory-wise. Tested on a 6 millions vertices scene.

    Using the CopyTo function on objects mesh to replace them is working (my meshes are only vertex and normal in float32 -> converted to float16). There is no visible changes so that's good.

    The thing is it does nothing performance wise. Worst, recalculating bounds afterwards (static batching utility or raycasting using bounds) is not working due to mesh vertex format (waiting for float32).

    Same behaviour happens on macOS and android.

    I wonder if this optimization lead is a dead-end due to unity relying on float32 everywhere and doing optimization based on this assumption ?
     
    AlejMC likes this.
  13. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    737
    So you are seeing lower memory usage if you click on your mesh and look in the inspector?

    Also, the major improvement/point of all this is improved memory usage. There will also possibly be a performance improvement on the GPU due to decreased memory pressure. But that is more dependent on a number of variables.
     
  14. AlexisDelforges

    AlexisDelforges

    Joined:
    Nov 30, 2021
    Posts:
    22
    Before :
    upload_2022-3-29_16-13-38.png
    After :
    upload_2022-3-29_16-14-43.png


    Yeah I do see an improvement; Float32*3 -> Float16*4 (*2 because I have positions and normals).
    Before with 889 vertices = 12*2*889 = 21336 bytes
    After with 889 vertices = 8*2*889 = 14224 bytes

    Note that Float16*3 is not accepted by Unity 2020.3.*, so the added Float16 does nothing.

    Performance rating was done using homemade FPS Meter; and it does not change anything.
    I could have use snapdragon profiler or arm studio to look into GPU stress ; but if the FPS won't drop as I'm GPU and vertex-bound, that means it probably didn't change anything GPU-wise.
     
  15. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    737
    "Note that Float16*3 is not accepted by Unity 2020.3.*, so the added Float16 does nothing" What do you mean?
     
  16. AlexisDelforges

    AlexisDelforges

    Joined:
    Nov 30, 2021
    Posts:
    22
    It does throw an error if you set the vertex attribute descriptor dimension value to 3 (which is expected as position is a float3) :
    ArgumentException: Invalid vertex attribute format+dimension value (Float16 x 3, data size must be multiple of 4)
     
  17. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    737
    Oh that's just because GPUs often expect data to be X byte aligned, where X is usually 4 bytes.
     
    chadfranklin47 likes this.
  18. rickomax

    rickomax

    Joined:
    Jun 11, 2013
    Posts:
    687
    Bump.
    Having the same issue.
    It seems to be a bug with SkinnedMeshRenderer.
     
    Last edited: Dec 21, 2022
  19. wudi_123

    wudi_123

    Joined:
    Oct 8, 2021
    Posts:
    3
    Hey man, sorrry to interrupt you because long time passed since this thread begins. I run into the same problem with you, when I invoke
    Code (CSharp):
    1. mesh.UploadMeshData(false);
    or
    Code (CSharp):
    1. mesh.UploadMeshData(true);
    , the rendering is ok but mesh vertex format is float32 instead of float 16. However, when I remove
    Code (CSharp):
    1. mesh.UploadMeshData();
    like you did, the rendering is abnormally but vertex format is float16. This is really weird and trouble me. Have you solved this? Or give some information about this. Thank you!