Support our Sponsors:

Go Back   Touch Arcade > Developer Discussion > Public Game Developers Forum

Reply
 
Thread Tools Display Modes
  #1  
Old 05-30-2014, 04:29 AM
Xaron Xaron is offline
Member
iPod Touch (4th Gen)
 
Join Date: Oct 2013
Posts: 95
Default Speed comparison: 2d Unity vs Monkey

Alright guys, I just did a quick 2d test with Monkey and Unity and would like to share the numbers:

I've used 2000 sprites which move randomly over the screen. Results are:

Unity 4.5
APK size: 8.404.358 bytes
FPS: ~20

Monkey v79c
APK size: 67,331 bytes
FPS: ~33 (previous value of 200 was wrong)

Unity C# code:
Code:
using UnityEngine;
using System.Collections;

public class Sprites : MonoBehaviour
{
  public GameObject _sprite;
  
  private ArrayList _spriteList;
  
  // Use this for initialization
  void Start()
  {
    _spriteList = new ArrayList();
    for( int i = 0; i < 2000; i++ )
    {
      GameObject sprite = (GameObject)Instantiate( _sprite, new Vector3( Random.Range( -6.0f, 6.0f ), Random.Range( -4.0f, 4.0f ) ), Quaternion.identity );
      _spriteList.Add( sprite );
    }
  }

  // Update is called once per frame
  void Update()
  {	
    for( int i = 0; i < _spriteList.Count; i++ )
    {
      GameObject s = (GameObject)_spriteList[i];
      Vector3 position = s.transform.position;
      float deltaTime = Time.deltaTime;
      position += new Vector3( Random.Range( -5.0f, 5.0f ), Random.Range( -5.0f, 5.0f ) ) * deltaTime;
      position.x = Mathf.Clamp( position.x, -6.0f, 6.0f );
      position.y = Mathf.Clamp( position.y, -4.0f, 4.0f );
      s.transform.position = position;
    }
  }
}
Monkey code:
Code:
Strict

Import mojo

Function Main:Int()
  New Game()
  Return 0
End Function

Class Sprite
  Field _img:Image
  Field _x:Float
  Field _y:Float
  
  Method New( img:Image, x:Float, y:Float )
    _img = img
    _x = x
    _y = y
  End Method
End Class

Class Game Extends App
  Field _sprite:Image
  Field _spriteList:List<Sprite>
  Field _resX:Float
  Field _resY:Float
  
  'FPS stuff
  Field _previousTime:Int
  Field _currentTime:Int
  Field _passedTime:Int
  Field _loopTimeAll:Int
  Field _fpsCounter:Int
  Field _fpsLastUpdate:Int
  Field _fps:Float
 
  Method OnCreate:Int()
    _sprite = LoadImage( "sprite.png" )
    _spriteList = New List<Sprite>
    _resX = Float( DeviceWidth() )
    _resY = Float( DeviceHeight() )
    For Local i:Int = 0 Until 2000
      Local s:Sprite = New Sprite( _sprite, Rnd( 0, _resX ), Rnd( 0, _resY ) )
      _spriteList.AddLast( s )
    Next
    _currentTime = Millisecs()
    SetUpdateRate(60)
    Return 0
  End Method

  Method OnUpdate:Int()
    Return 0
  End Method
 
  Method OnRender:Int()
    _previousTime = _currentTime
    _currentTime = Millisecs()
    _passedTime = _currentTime - _previousTime

    Cls()
    For Local s:Sprite = EachIn _spriteList
      Local dx:Float = Rnd( -0.1, 0.1 ) * _passedTime
      Local dy:Float = Rnd( -0.1, 0.1 ) * _passedTime
      s._x += dx
      s._y += dy
      s._x = Clamp( s._x, 0.0, _resX )
      s._y = Clamp( s._y, 0.0, _resY )
      DrawImage( s._img, s._x, s._y )
    Next

    DrawText( _fps + " FPS", 10, 10 )
    updateFPS()
    Return 0
  End Method
 
  Method OnSuspend:Int()
    Return 0
  End Method
 
  Method OnResume:Int()
    Return 0
  End Method

  Method updateFPS:Void()
    _loopTimeAll += _passedTime
    
    _fpsCounter += 1
    If( ( _currentTime - _fpsLastUpdate ) > 500 )
      Local renderTime:Float = Float( _loopTimeAll ) / Float( _fpsCounter )
      If( renderTime < 1.0 ) Then renderTime = 1.0
      _loopTimeAll = 0
      _fpsLastUpdate = _currentTime
      _fps = 1000.0 / renderTime
      _fpsCounter = 0
    End If
  End Method
End Class
Sample APKs:
Unity: http://www.leidel.net/dl/monkey/2dtest/unitytest.apk
Monkey: http://www.leidel.net/dl/monkey/2dtest/monkeytest.apk

Project files:
http://www.leidel.net/dl/monkey/2dtest/unityproject.zip
http://www.leidel.net/dl/monkey/2dte...keyproject.zip

Actually I'm pretty shocked how much faster Monkey is!

Test device was a Sony Xperia S. I'd be interested in more comparison values and stuff how (especially) the Unity code could be done better?

Last edited by Xaron; 05-31-2014 at 02:59 AM..
Reply With Quote
  #2  
Old 05-30-2014, 05:01 AM
POLYGAMe POLYGAMe is offline
Developer
iPad (4th Gen), iOS 7.x
 
Join Date: Jul 2010
Location: Auckland, New Zealand
Posts: 520
Default

Now do a test with 200k polygons and see which is faster
Reply With Quote
  #3  
Old 05-30-2014, 05:13 AM
Xaron Xaron is offline
Member
iPod Touch (4th Gen)
 
Join Date: Oct 2013
Posts: 95
Default

That was not the question but if you like you can create such a project in Unity and send it to me please? I'd be interested!
Reply With Quote
  #4  
Old 05-30-2014, 05:44 AM
Destined Destined is offline
Senior Member
iPhone 5, iOS 6.x
 
Join Date: Aug 2013
Posts: 744
Default

Quote:
Originally Posted by Xaron View Post
That was not the question but if you like you can create such a project in Unity and send it to me please? I'd be interested!
I have run projects in unity with over 200K poly's fine.

I find the results of your test interesting, however that test isn't really using any of the features of either engine. If you were just going to do that you might as well do it in native code.

I would expect in this monkey to be faster, but I am surprised how much faster. The benefits of using unity come from using the API.

Free html5 is going to be a big benefit over unity because it is unlikely the unity html5 implementation will be as flexible as monkey (because it will require webgl)

Last edited by Destined; 05-30-2014 at 05:47 AM..
Reply With Quote
  #5  
Old 05-30-2014, 06:54 AM
OnlyJoe OnlyJoe is offline
Member
iPhone 6 Plus, iOS 8.x
 
Join Date: Sep 2013
Location: Auckland
Posts: 96
Default

The difference is going to be in how each engine treats objects rather than polygons. And how they are dealing with them to push them to OpenGL.

Basically the most expensive thing that game engines do is matrix maths on object positions. My guess is that Unity is doing some kind of matrix stack for each object based on its transform, the camera position and parent objects. Then pushing each sprite to the GPU one at a time.

So my guess is that unity will build a matrix for each object by building a transform, scale and rotation matrix and multiplying them together. This will then get multiplied by the camera matrix, to generate the view matrix for the object. This is then multiplied by the projection matrix, and pushed to the GPU. As it is a 3d engine it is using 4x4 matrix, rather than the 3x3 that a 2d engine can use. So it needs to multiply 5 matrix together before it has the final matrix for the GPU, or 80 multiply operations per sprite per frame, or 16000 for all the sprites. This can be optimised if the matrix object is designed to skip identity matrix multiplications, but most I have seen are not. This matrix maths start to get very expensive as the number of objects gets larger. Plus using a view matrix for each object means that you have to render each object in a separate call to the GPU. Which is fine for a 3d engine, where you are pushing a complex vertex and index buffer, with maybe a few textures. In 2d however you can greatly optimize this.

So with Monkey it is probably not using a camera, and is probably not using a matrix for each object. Instead it probably just creates 4 2d points whenever you call the draw command, using the x,y and rotation of the object. These points just get added to the end of a vertex, index and texture buffer that is being built each frame. Every time a texture changes a new vertex buffer gets started. But seeing as you are using only one texture, they probably all get added to the same buffer. When the scene is drawn this single vertex buffer gets pushed to the GPU and it means everything is drawn in a single call. Much like how a particle emitter renders all its particles. This will give it a massive speed boost over how a 3d engine works, but puts more limitations on what can be done with the sprites.

One way to test this better would be to use a range of textures for the sprites, and make sure no two sprites with the same texture are drawn in sequence. This will prevent the engine from optimizing its draw calls so much. But you will probably find that it is still faster because it doesn't need to do so much matrix maths.
Reply With Quote
  #6  
Old 05-30-2014, 09:39 AM
Mike77 Mike77 is offline
Junior Member
iPhone 4S, iOS 6.x
 
Join Date: Jun 2013
Posts: 14
Default

Ping from the Unity forum.

First, you can't have 200 FPS on Android, max 60.

Second, the author is comparing FPS in Unity to OnRender execution per second in Monkey. This is wrong, these metrics are different and can't be compared.

Third, no draw call optimization can help with that much fillrate, the test repaints 100% of the screen more than 10 times, mobile devices can't handle that much with a good FPS rate.
Reply With Quote
  #7  
Old 05-30-2014, 02:37 PM
Xaron Xaron is offline
Member
iPod Touch (4th Gen)
 
Join Date: Oct 2013
Posts: 95
Default

Mike, I got your points even though they don't convince me yet. For the Unity FPS computation I use the HUDFPS script from here: http://wiki.unity3d.com/index.php?title=FramesPerSecond

And of course Android is fixed to 60 fps but that doesn't change the fact than OnRender in Monkey only takes 5ms per loop.

Actually it's almost the same stuff I use. frames per second are usually 1/rendertime. I guess you mean that I don't measure the rendertime but just this loop and rendering happens after that, did I get you right?

If so, how about this test: I will render as much as many sprites till I have one Update call (in case of Monkey OnRender) per second. Would that be the same? In that case I would get the numbers of sprite per second.
Reply With Quote
  #8  
Old 05-30-2014, 04:27 PM
Destined Destined is offline
Senior Member
iPhone 5, iOS 6.x
 
Join Date: Aug 2013
Posts: 744
Default

some interesting tests would be comparing the game engine side. Like try may 100 2D rigidbody sprites with physics and see how they compare.
Reply With Quote
  #9  
Old 05-30-2014, 06:36 PM
OnlyJoe OnlyJoe is offline
Member
iPhone 6 Plus, iOS 8.x
 
Join Date: Sep 2013
Location: Auckland
Posts: 96
Default

@Xaron, you can't just measure the time that the OnRender function takes. This may only be buffering commands on the OpenGL pipeline, and it won't be until the screen needs to render that these get executed. You need to measure the frame rate for each cycle, or do a glFlush() call at the end of the OnRender function to make sure you are getting the real draw time. But even then you can't just measure the OnRender part, as the engine might be doing all kinds of other processing as well in different functions. But you are still going to find that Monkey is probably faster, because it is designed for 2d rather than 3d.

As you mentioned before, a different way to compare this would be to see how many objects need to be created to bring the frame rate down to a set level. Say 30 frames per second. When measuring the time difference at the same point in your code, not just the time it takes to run the one function.

Last edited by OnlyJoe; 05-30-2014 at 06:49 PM..
Reply With Quote
  #10  
Old 05-31-2014, 02:19 AM
Xaron Xaron is offline
Member
iPod Touch (4th Gen)
 
Join Date: Oct 2013
Posts: 95
Default

Will do that next, OnlyJoe!

edit: I have to admit that I was indeed wrong with my fps counter. I've adapted the fps code to measure the time between two render calls now. Using this code, I get 33 fps for Monkey which is more realistic and still 65% faster than Unity. Thanks again and sorry for that! I'm still very satisfied with these results.

Last edited by Xaron; 05-31-2014 at 02:52 AM..
Reply With Quote

Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Copyright 2012, TouchArcade.com, LLC.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2014, vBulletin Solutions, Inc.
Copyright 2008 - 2011, TouchArcade.com. Privacy Policy / DMCA Copyright Agent