Procedural mesh update slow on iphone 4

Discussion in 'iOS Development' started by yuriks, Nov 14, 2011.

  1. yuriks

    yuriks

    New Member

    Joined:
    Nov 11, 2011
    Messages:
    25
    Hello,

    I'm using Prime31's UIToolkit to create the user interface and HUD of my iOS game. All was working fine until last week when suddenly I would get horrible lag bursts whenever I moved or updated an interface element, which you can see in this image, but only when running on the iPhone 4 or lower, on PC and the iPad 2 it's fine. (So, moving the joystick thumb, or updating a button with the "pressed" image for example will cause horrible stuttering.)

    I've already tried rolling back my project to an earlier version yet the problem remains, so it must be caused by some change in Unity or iOS. I've tracked down the slowdown to updating the procedural mesh used for the UI. Even with only a few vertices the slow down still happens, and if I comment out the "mesh.vertices = tempVertices" assignment the slow down goes away (and the mesh isn't updated, of course), so my bottleneck isn't on the generation of the new vertex data, but on Unity's update. Anyone knows what's up? I've been banging my head on this for days now.
  2. froodydude

    froodydude

    New Member

    Joined:
    Sep 22, 2011
    Messages:
    27
    I don't think you have selected one of the spikey frames in the image you supplied so I can't see for sure what's going on however....

    We're seeing the same issue and it looks like a problem in Unity related to the way it's updating OpenGL buffers and then trying to render from them which is causing massive stalls (it could potentially be a problem in the gl driver instead). One work around which sounds counter intuitive is to ensure that rendered geometry is always taking the dynamic batching path because this will cause it not to render from the OpenGL buffer and instead render from client memory which avoids the stall. To do this you need to render another mesh (that will not get culled) that uses the same material as the GUI elements that are causing problems. We're currently searching for another work around but right now that's the best we have I'm afraid.

    Hope that helps
  3. yuriks

    yuriks

    New Member

    Joined:
    Nov 11, 2011
    Messages:
    25
    It not only helps, I want to hug you right now! :D

    I created another dummy mesh object that used the same material and consisted only of a single 0-area triangle, that made Unity dynamic batch both and sure enough, the slowdown is gone. I reported the bug to Unity, so hopefully it gets fixed some time in the future...

    Maybe Unity gives a different hint when uploading the vbo (GL_STREAM_DRAW instead of GL_STATIC_DRAW, maybe), causing the GL driver to behave better.

    Thanks again.
  4. froodydude

    froodydude

    New Member

    Joined:
    Sep 22, 2011
    Messages:
    27
    Awesome!

    We've also found that you can ping-pong/double buffer meshes to avoid the problem but this has other drawbacks such as increased memory and hassle to get things to not cache the mesh they want to write to. This works because you have two buffers and so you're updating the one the GPU has finished with, where as with a single mesh it is trying to update a buffer in use by the GPU, which causes a stall.

    I do hope Unity fix the issue, it's a major pain.
  5. TrialByFun

    TrialByFun

    Member

    Joined:
    Jun 23, 2009
    Messages:
    89
    I've got my own GUI solution which is pretty similar to UIToolKit and I've also been suffering this problem (but not realised how to get around it until now),

    updating and reassigning the mesh vertices every frame would cause a Mesh.CreateVBO spike ( 5 - 11ms on iPod 4 ) which seemed way more than I expected.

    So, going off of froodydude's suggestion I tried implementing the mesh double buffering (write to one mesh one frame, and assign that to the mesh filter, then write to another the next frame and assign that.. etc), this took Mesh.CreateVBO down to 0.82 - 1.01ms when assigning the vertices every frame.

    Ive got a quite a few dynamic meshes so i'll be rolling this solution out to the other classes. I don't really like having to do it this way but it's working!

    I guess it would be good if there was a way for us to tell the engine "hey i'm probably going to be updating this mesh every frame, so treat it differently somehow"?

    Thanks froodydude for the tip!
  6. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Messages:
    10,454
    I wondered about this problem myself, and ended up with the gui using a boned mesh.
  7. badawe

    badawe

    New Member

    Joined:
    Jan 17, 2011
    Messages:
    277
    Guy, I think i get the same issue on my project.

    Our game is 2D, but i can't find what is the problem on iPhone 4, the performance of the game on iPad 1 is fine! But on iPhone 4 i get get a crap performance..

    If i create one cube, and put all my materials on that, can pre warn the textures? or i misunderstood ?
  8. Ben BearFish

    Ben BearFish

    Member

    Joined:
    Sep 6, 2011
    Messages:
    454
    Would you mind showing me what files you modified in UIToolkit to achieve this? My team is facing the same issue. I'm trying to understand how do you create a "dummy mesh object that used the same material and consisted only of a single 0-area triangle, that made Unity dynamic batch both and sure enough, the slowdown is gone". Thank you.
  9. froodydude

    froodydude

    New Member

    Joined:
    Sep 22, 2011
    Messages:
    27
    Well it might be the same issue. If you have a pro licence use the Unity profiler and look for very high amounts of time spent in CreateVBO when it spikes/is slow. If it is this then you can fix by ensuring rendering takes the dynamic path. Unity will take the dynamic path when two or more meshes share the same material and are not culled. Note that this is a per-frame decision. The underlying problem is to do with buffer updates and how it submits geometry, it's not a problem with textures or materials.

    So for every mesh you change dynamically that frame you need to ensure that another mesh which uses the same material is also rendered and is not culled. This mesh can just contain a 0 area tri but must _not_ be culled.

    In pseudo code you could solve like this (but it can be improved upon):

    // One time
    - Create a mesh for every material in the game, put one 0-area triangle in in and set the culling data to be very large.

    // every frame
    - For every material used this frame, send the previously created mesh that uses that material.

    Like I say this can be improved, only dynamic mesh updates are affected and if there are already two non-culled meshes using a material being rendered then you won't need to send the extra mesh either.

    We ended up fixing by double buffering meshes, but this comes at a memory cost which in our case we felt was acceptable.

    I hope that helps
  10. yuriks

    yuriks

    New Member

    Joined:
    Nov 11, 2011
    Messages:
    25
    I haven't tested it yet (I'm near a deadline, not the best time to be upgrading to beta unity versions) but it seems that the Unity 3.5 preview released today has a relevant entry in the release notes:

  11. GTHaxor

    GTHaxor

    New Member

    Joined:
    Dec 12, 2011
    Messages:
    60
    So for a noob who doesn't have a clue of what any of this is.. what are the steps I need to take to use this work-around?

    What I did was add a cube object, reduce it's scale to (0, 0, 0), and added every material to it's mesh renderer component. I'm guessing this is incorrect because I'm still getting 30-60% on Mesh.CreateVBO.
  12. froodydude

    froodydude

    New Member

    Joined:
    Sep 22, 2011
    Messages:
    27
    I suggest doing the whole thing in code. Create a mesh in code and add just one zero area triangle and material to it. It's possible Unity treats a scale of 0, 0, 0 in such a way that it doesn't render the object (which is why the problem doesn't go away). It does not however make the same tests per triangle. You also need to set the bounds of the mesh to something large so it doesn't get frustum culled.

    Note that really you only want to render these created meshes on problem materials and only when something else using that material is being rendered.
  13. GTHaxor

    GTHaxor

    New Member

    Joined:
    Dec 12, 2011
    Messages:
    60
    Still a bit confused.. Not much, but here's what I have so far.

    Code (csharp):
    1.     //Vectors
    2.     var vert:Vector3[] = [Vector3(0,0,0), Vector3(0,0,0), Vector3(0,0,0)];
    3.     var tri:int[] = [0, 1, 2];
    4.    
    5.     //Mesh
    6.     m = new Mesh();
    7.     m.vertices = vert;
    8.     m.triangles = tri;
    9.    
    10.     //??
    Not sure how to add a material to the mesh. Do I need to attach the mesh to a renderer?
  14. froodydude

    froodydude

    New Member

    Joined:
    Sep 22, 2011
    Messages:
    27
    Yes that looks about right, now:

    1) Add a MeshRenderer to the object and set a material on the mesh renderer.
    2) In code set the mesh on the MeshRenderer.
    3) You also want to set the bounds on the mesh using something like mesh.bounds = new Bounds( 100000.0f, 100000.0f ). This is just ensuring the object is not culled, but there are other ways of doing this such as parenting the object to the camera.

    Note you can add multiple materials to the mesh renderer if you want and use SetTriangles to set each submesh (which the docs claim is necessary).

    Once you've done steps 1 - 3 it should get rid of the spike.
  15. GTHaxor

    GTHaxor

    New Member

    Joined:
    Dec 12, 2011
    Messages:
    60
    I'ved added a MeshRenderer component to the empty object I made in the scene/editor and added the problematic material to it. The same empty object also has the script attached to it. Now, I'm not sure how to do step 2, "In code set the mesh on the MeshRenderer".

    Also, you lost me where you said "you can add multiple materials to the mesh renderer if you want and use SetTriangles to set each submesh". I added multiple materials to the MeshRenderer component but again find myself stuck and out of luck.
  16. froodydude

    froodydude

    New Member

    Joined:
    Sep 22, 2011
    Messages:
    27
    Sorry my mistake, you need to set the mesh on a MeshFilter so you need to add one of those to the object too.
  17. GTHaxor

    GTHaxor

    New Member

    Joined:
    Dec 12, 2011
    Messages:
    60
    @froodydude, thanks for your help so far. I just have a little bit left to make this work:

    Here's what I have so far:
    Code (csharp):
    1.     //Vectors
    2.     var vert:Vector3[] = [Vector3(0,0,0), Vector3(0,0,0), Vector3(0,0,0)];
    3.     var tri:int[] = [0, 1, 2];
    4.    
    5.     //Mesh
    6.     m = new Mesh();
    7.     m.vertices = vert;
    8.     m.triangles = tri;
    9.    
    10.     //MeshFilter
    11.     goObject.AddComponent(MeshFilter);
    12.     filter = goObject.GetComponent(MeshFilter);
    13.     filter.mesh = m;
    14.    
    15.     //MeshRenderer
    16.     mRenderer = goObject.GetComponent(MeshRenderer);
    17.    
    18.     //Bounds
    19.     mRenderer.bounds.SetMinMax(Vector3(-10000f, -10000f, -10000f), Vector3(-10000f, -10000f, -10000f));
    20.     m.bounds.SetMinMax(Vector3(-10000f, -10000f, -10000f), Vector3(-10000f, -10000f, -10000f));
    21.  
    I tried just running this but the MeshRenderer is still using up a ton of CPU. I have 8 different materials added to the MeshRenderer component in the editor. Do I need to do anything with them in the script? If so, could you give me a short snippet of code that would do this? I couldn't find any references to this anywhere. Thanks again
  18. froodydude

    froodydude

    New Member

    Joined:
    Sep 22, 2011
    Messages:
    27
    Well from the docs it would look something like:

    Code (csharp):
    1. for( int index = 0; index < mRenderer.materials.Length; ++index )
    2. {
    3.     m.SetTriangles( tri, index );
    4. }
    There is also a distinction between materials and shared materials and tbh I'm not all that clear on what is set when, and what impact this has so you might need to experiment with using both materials and shared materials and read the docs to understand what's going on. My feeling is you need to ensure materials are correctly shared but as I didn't fix the problem this way I can't say for certain.

    What you really need to do is for a frame capture in XCode so you can see the GL calls, this will tell you what the renderer is doing with your objects. The other thing to do is strip the problem back so you only have the bare minimum rendered to cause the problem. Once you have this you can iterate until the problem goes away and then add in another element and see what happens.

    This problem does look like it's potentially fixed in Unity 3.5 btw.
  19. GTHaxor

    GTHaxor

    New Member

    Joined:
    Dec 12, 2011
    Messages:
    60
    Thank you do much. You've been very helpful and patient with me. Strange thing is that I'm using the 3.5 dev preview and still getting huge numbers for CreateVBO. Perhaps I'm having a completely different issue. Either way, I'll try what you've suggested and iterate till I find the culprit. Thanks for the help!
  20. TrialByFun

    TrialByFun

    Member

    Joined:
    Jun 23, 2009
    Messages:
    89
    just seen a note in the 3.5 RC1 build notes [ Just available now for download ]

    "Graphics: Fix performance regressions with dynamic/static batching in multithreaded renderer vs. single thread"

    I haven't had a chance to test this yet, but will do in the next couple of hours.
    Will reply in here if this has made a difference.
  21. GTHaxor

    GTHaxor

    New Member

    Joined:
    Dec 12, 2011
    Messages:
    60
    Downloaded the new 3.5.0f1 (3.5 RC1) and I'm still getting spikes in CreateVBO (60%). Anyone else having the same issue?
  22. wightwhale

    wightwhale

    Member

    Joined:
    Jul 28, 2011
    Messages:
    186
    I translated goodtimeshaxor's code to c# for anyone who is interested. This is only for specific materials which are giving you trouble. Fixed my problem :D

    Code (csharp):
    1.  
    2. Mesh m;
    3. Vector3 [] zeroVerts = {new Vector3(0,0,0),new Vector3(0,0,0),new Vector3(0,0,0)};
    4. int [] triArry = {0,1,2};
    5.  
    6. void Start()
    7. {
    8.         //load the material
    9.         lineMat = (Material)Resources.Load ("Balloon Line", typeof(Material));
    10.  
    11.         //make a new 0 area mesh triangle
    12.     m = new Mesh ();
    13.     m.vertices = zeroVerts;
    14.     m.triangles = triArry;
    15.  
    16.         //make a new game object to attach the mesh to
    17.     go = new GameObject ("Inivisible Triangle");
    18.  
    19.         //add the necessary mesh rendering components
    20.     go.AddComponent ("MeshFilter");
    21.     go.AddComponent ("MeshRenderer");
    22.  
    23.         //assign the mesh to the game object
    24.     MeshFilter mFilter = go.GetComponent<MeshFilter> ();
    25.     mFilter.mesh = m;
    26.        
    27.     MeshRenderer mRenderer = go.GetComponent<MeshRenderer> ();
    28.        
    29.         //set the bounds to a giant area so the object won't get culled ever
    30.     mRenderer.bounds.SetMinMax (new Vector3 (-10000f, -10000f, -10000f), new Vector3 (10000f, 10000f, 10000f));
    31.     m.bounds.SetMinMax (new Vector3 (-10000f, -10000f, -10000f), new Vector3 (10000f, 10000f, 10000f));
    32.    
    33.         //assign the material to the new invisible triangle game object
    34.     mRenderer.material = lineMat;
    35. }
    36.  
    Though this doesn't really seem to work on certain shaders. Not really sure why.
    Last edited: Jan 27, 2012
  23. GTHaxor

    GTHaxor

    New Member

    Joined:
    Dec 12, 2011
    Messages:
    60
    T_T It works for you but doesn't work for me?! That's messed up =P

    Do you know which shaders work and which don't? Maybe that's my issue.
  24. wightwhale

    wightwhale

    Member

    Joined:
    Jul 28, 2011
    Messages:
    186
    Looks as I've forgotten part of the pseudocode which was posted by froodydude

    // every frame
    - For every material used this frame, send the previously created mesh that uses that material.

    I'm not exactly sure how to code this part.
  25. gametr4x

    gametr4x

    Member

    Joined:
    Apr 22, 2009
    Messages:
    69
    I'm testing this with SpriteManager 2 animated PackedSprites, and I'm getting large CreateVBO spikes on all my animated sprites, which appears to happen as long as only one of them is on screen.

    Tried the code above, with the addition of setting the material to "mRenderer.sharedMaterial", but it didn't fix anything for me. I'm using a custom shader, so that could be a possible cause of the problem. That shader doesn't do much though, aside from a HSV transform in the pixel shader. The rest is just basic alpha blended rendering.

    EDIT: Tested it with nothing in the scene but the sprite, as well as with two of the same sprite (both animating). Problem occurs in both situations.

    Is it even possible for two animating sprites to dynamic batch together?
    Last edited: Jan 30, 2012
  26. daveUnit9

    daveUnit9

    New Member

    Joined:
    Apr 1, 2010
    Messages:
    8
    I may have an explanation as to why attempting to force dynamic batching with invisible triangles does not always work. There are multiple conditions that renderers must fulfil in order to be dynamically batched. Adding invisible triangles is unlikely to help fulfil the last condition.

    * The renderers must use the same material
    * The renderers must have identical scales
    * All renderers using transparent shaders are depth-sorted as one collection. Any objects that would otherwise be batched, but are not in one continuous group within the depth ordering can not be batched.

    However I think there may be more conditions as I have dynamic meshes that fulfil these conditions but still induce high Mesh.CreateVBO processing.

    The double-buffered mesh solution in this thread definitely works, but I don't really see it as an option to hack EZGUI/SM2 to use this solution.

    Please fix this Unity!
  27. TrialByFun

    TrialByFun

    Member

    Joined:
    Jun 23, 2009
    Messages:
    89
    I've logged a bug for this ( 439770 ) and that assigning vertex colours also takes a massive amount of time.

    So, a little tip for anyone using vertex colours.

    You'll notice that assigning vertex colours takes a massive amount of time compared to any other attribute on the Mesh class. So at the moment i'm assigning their values to the tangents of the mesh rather than the colour array and read them in through the glsl shader.

    This saved me 8ms ( in worst case ) on the 3GS. [ Note I believe if you write the shader in CG then the tangents get normalised somehow, so this hack only works in GLSL that I know of ]

    Hope this helps.
  28. yuriks

    yuriks

    New Member

    Joined:
    Nov 11, 2011
    Messages:
    25
    For those who were asking me for more info on how to implement this, sorry for not answering before, I've been busy lately. My solution is pretty similar to what wightwale posted, and unfortunately, I found it to not be very reliable. For now I think implementing double buffering is the best option, if possible.
  29. daveUnit9

    daveUnit9

    New Member

    Joined:
    Apr 1, 2010
    Messages:
    8
    It seems that this issue is not fixed with Unity 3.5 .... In fact I think it might be even worse now.
  30. TrialByFun

    TrialByFun

    Member

    Joined:
    Jun 23, 2009
    Messages:
    89
    It appears to be firing now in places where it wasn't before. I'm checking over my code again now ( i don't think i've changed anything ) but i'm getting Mesh.CreateVBO firing even with my previous fix.

    *Sadface*

    On the plus side, the bug i reported is still listed as Open, which means at some point it will get fixed.
  31. Mantas Puida

    Mantas Puida

    Unity Technologies

    Joined:
    Nov 13, 2008
    Messages:
    1,190
    It is design issue, we will try to solve it for next major release.
  32. TrialByFun

    TrialByFun

    Member

    Joined:
    Jun 23, 2009
    Messages:
    89
    Cool! Design issue on your side or mine? I.e am I / we using the mesh class wrongly in our code, and there is a better way to create dynamic meshes?
  33. Mantas Puida

    Mantas Puida

    Unity Technologies

    Joined:
    Nov 13, 2008
    Messages:
    1,190
    Some people reported improved performance when using skinned meshes instead of regular ones.
  34. daveUnit9

    daveUnit9

    New Member

    Joined:
    Apr 1, 2010
    Messages:
    8
    Meaning Unity 4?

    I'm surprised you don't assign more priority to this issue. It affects so many of the popular tools on the Asset store i.e. EZGUI, SpriteManager, RageSpline... anything with dynamic meshes is affected by this issue.
  35. Mantas Puida

    Mantas Puida

    Unity Technologies

    Joined:
    Nov 13, 2008
    Messages:
    1,190
    mean NOT 3.5.1
  36. ddeaco

    ddeaco

    New Member

    Joined:
    Jan 17, 2012
    Messages:
    44
    I appear to have hit a performance bottleneck. My target platform is iOS. Testing on iPad first Gen.

    I would like to distort a 128x128 grid (16k vertices) on every frame, Everything is running at 60fps until I set the mesh.vertices. Performance drops to 25fps.

    I have also tried setting mesh.colors and distorting the vertices in a vertex shader, but this also creates the same fps drop. (Vertex shader runs at 60fps with baked vertex colors).

    I've test a 64x64 grid, fps 33. and 32x32 grid which achieves 60fps again.

    Is their any way that I can, ether update a meshes vertices in an efficient way or get the vertex color array to the shader in a different way?

    @SBurgess How do you access the tangents inside the Vertex shader?
  37. TrialByFun

    TrialByFun

    Member

    Joined:
    Jun 23, 2009
    Messages:
    89
    If you're writing GLSL which is what all my shaders are in ( because i know i'm only targeting iDevices) then you use "attribute vec4 Tangent" in the vertex shader.

    An example below.


    You can't avoid Mesh.CreateVBO completely ( at the moment ), but you can minimise it with the double buffer trick. On top of that then further reduce the cost by assigning vertex colour rgba to the tangents.

    So in your mesh instead of assigning to the Mesh.Color array you assign to the Tangents array ( assuming you're not using it for something else ).

    tangent.r = color.r;
    tangent.g = color.g;
    tangent.b = color.b;
    tangent.a = color.a;

    ( or you could store colours as Vector4 to start with and never use the Color struct)

    I assume the reason assigning colours is so slow is that Internally unity converts to Color to Color32 which has a conversion cost.


    Now this is total hack and I hope one day we won't need to do this to get that last ounce of speed.

    Hope this helps.
  38. ddeaco

    ddeaco

    New Member

    Joined:
    Jan 17, 2012
    Messages:
    44
    Thanks Burgess, I've updated my GLSL shader.

    Which improved the fps from 25 to about 32 on the iPad first gen.

    Could you example the double buffing trick in some more detail?

    Thanks
    David.
  39. TrialByFun

    TrialByFun

    Member

    Joined:
    Jun 23, 2009
    Messages:
    89
  40. numberkruncher

    numberkruncher

    Member

    Joined:
    Feb 18, 2012
    Messages:
    802
    Hey guys, I may have found something (or I might not understand what I am actually doing lol)

    But updating the procedural mesh using a coroutine that executes the same number of mesh updates seems a lot more efficient than using the Update function:

    Code (csharp):
    1.  
    2. class SlowExampleBehaviour : MonoBehaviour {
    3.  
    4.     Mesh mesh;
    5.     float timer;
    6.  
    7.     // ...
    8.  
    9.     void Update() {
    10.         timer += Time.deltaTime;
    11.         if (timer < 0.07f)
    12.             return;
    13.  
    14.         timer = 0.0f;
    15.  
    16.         UpdateProceduralMesh();
    17.     }
    18. }
    19.  
    20. class FastExampleBehaviour : MonoBehaviour {
    21.  
    22.     Mesh mesh;
    23.     WaitForSeconds waiter;
    24.  
    25.     void Start() {
    26.         // ...
    27.  
    28.         // Create here to avoid GC later
    29.         waiter = new WaitForSeconds(0.07f);
    30.         StartCoroutine(Animator());
    31.     }
    32.  
    33.     IEnumerator Animator() {
    34.         while (true) {
    35.             UpdateProceduralMesh();
    36.             yield return waiter;
    37.         }
    38.     }
    39. }
    40.  
    On iPhone 4 with SlowExampleBehaviour the fps is 24-26, with FastExampleBehaviour the fps is 30-32.

    Edit: With thanks to Karel in IRC it seems that the time it takes to update the mesh is longer than 0.07 seconds which means that the `Update` approach is stacking mesh updates. Whereas the coroutine guarantees at least 0.07 seconds will pass before the mesh is next updated and thus resulting in a way better fps.
    Last edited: Oct 13, 2012
  41. froodydude

    froodydude

    New Member

    Joined:
    Sep 22, 2011
    Messages:
    27
    Interesting but I think you ought to rewrite your test. Remove all the timer stuff and just go as fast as you can (use yield return null for the coroutine). Obviously you need to ensure your test is CPU bound and not GPU bound. This should show whether or not there is any actual advantage of coroutines over Update.
  42. numberkruncher

    numberkruncher

    Member

    Joined:
    Feb 18, 2012
    Messages:
    802
    I think that there is little difference between the two options because I think that this is what was happening:

    Code (csharp):
    1.  
    2. interval = 0.07f;
    3. timer = 0.0f;
    4.  
    5. void Update() {
    6.    timer += Time.deltaTime;
    7.    if (timer < interval)
    8.       return;
    9.  
    10.    timer = 0.0f;
    11.  
    12.    // If it takes longer than 0.07f to complete the following action then the next Update
    13.    // will update the mesh again.
    14.    UpdateMesh();
    15. }
    16.  
    This was avoided with the co-routine because the co-routine executes and the waits a minimum of the specified time. This means that there is definately 0.07f seconds between updating the mesh which made a lot of difference performance wise.

    The co-routine method with different update intervals. These are some quick readings that I have just taken from the iPhone 4:

    Interval: FPS on iPhone:
    0.00 1-4fps (equivalent of yielding null)
    0.01 1-4fps
    0.02 1-5fps
    0.05 5-14fps
    0.07 25-27fps
    0.08 29-31fps
    0.1 30fps

    Edits:

    By the way this is with 1 mesh with 40,000 vertices where only the UV coordinates are being updated each time.

    Interesting, my 0.07 test this time performed less efficiently than previously...
    Last edited: Oct 15, 2012