To OpenGL or to SurfaceView

What is OpenGL?

OpenGL is a library and standard used for developing games that render animated graphics fast. It is a c library with a java wrapper for use on android. It takes advantage of the graphics processor and its associated memory.

What is SurfaceView?

SurfaceView is an Android view that allows continuous rendering for graphics in a separate thread from the user interface thread – so as not to block user input. Unlike OpenGL, SurfaceView uses standard android canvas and related draw, paint, matrix, camera etc. They are a lot easier to use than OpenGL. But, it is not as optimized for performance.

The Story

I wrote First Grade Math bubbles game with OpenGL last year. I was a fan of what could be accomplished with OpenGL and how fast the graphics looked. I also felt rather proud of building such complex code. Then, in March, I was writing my Android lecture on SurfaceView. I thought I’d give the class a taste of what SurfaceView cannot do, and when they’d have to go to OpenGL because its so much faster. I wrote my simple sample in Surface View, and logged the Frames Per Second (FPS) expecting it to be low – 15 or 20 FPS. To my surprise it was 60 frames per second. Most movies are 28 or 30 FPS, so, 60 was more than sufficient for the games we build. My inner engineer didn’t quite accept it. The more complex OpenGL code had to be better, right? Not really. There was no getting away from the data.

Around the same time as I was figuring out that I could have written my bubbles game a lot simpler, Katherine started building effects for the next game – Nebula Kids Phonics. She started with OpenGL as I had with my bubbles game. She actually stuck with it a lot longer than I had, and figured out a lot more things she could do – images with transparent sections, encapsulating textures etc. But, by then, I was convinced that the complexity of OpenGL was not worth it – that we could do what we wanted with SurfaceView, and with less bugs and less issues.

The decision

Engineers tend to believe that the more complex the code, the better it is, the less likely anyone else can do the same. We both fell into that trap. But, we realized that by simplifying the code we could build more games, and more learning content instead of more framework. So, we changed the underlying code to use SurfaceView, and we both built many screens for many different games, and a lot of learning content to go with all of that for Nebula Kids Phonics – an overall better game.

There was several issues that we solved along the way. Some tips and tricks we discovered –

  • Use a queue to pass touch events and process them on the rendering thread. It prevents data synchronization headaches.
  • Make any primitive variables that store rendering coordinates, timer or state volatile. Otherwise you see animations jump around randomly sometimes.
  • Pool objects to prevent frequent garbage collection.
  • Load bitmaps at the start of the game play sequence. Keep them in memory till the next game.
  • Use canvas drawing functions for background with shaders for gradients instead of full screen images. They look and perform better.

Did we make the right choice?

What do you think? Did we make the right decision? Is there other graphics and animations frameworks we should be looking at? If you’d like to give it a try, below is some sample code to try out SurfaceView.

Getting started with SurfaceView

Here is the bare-bones sample I use to teach surface view for the Android class. It is a simple view that shows a ball bouncing around the screen.

First the activity that delegates rendering to the View.

public class PopActivity extends Activity {

 PopView popView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_pop);
   popView = (PopView) findViewById(R.id.popview);
 }

 @Override
 protected void onResume() {
   super.onResume();
   popView.resume();
 };

 @Override
 protected void onPause() {
   super.onPause();
   popView.pause();
 };
}

next, the layout


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context=".PopActivity" >

<edu.uw.pop.view.PopView
 android:id="@+id/popview"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 />

</RelativeLayout>

and lastly, the SurfaceView derived PopView that launches a rendering thread, which continuously draws and updates.

public class PopView extends SurfaceView implements Runnable {
  Paint paint;

  int screenheight;
  int screenwidth;
  float density;

  volatile int cx;
  volatile int cy;
  int radius;
  volatile int opx = 1;
  volatile int opy = 1;

  volatile boolean running = false;
  SurfaceHolder holder;
  Thread renderThread = null;

  public PopView(Context context, AttributeSet attrs) {
    super(context, attrs);
    holder = getHolder();

    DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
    screenheight = dm.heightPixels;
    screenwidth = dm.widthPixels;
    density = dm.density;
    radius = Math.round(20*density);

    paint = new Paint();
    paint.setStyle(Paint.Style.STROKE);
    paint.setColor(Color.GREEN);
    paint.setStrokeWidth(10);
 }

public void resume() {
  running = true;
  renderThread = new Thread(this);
  renderThread.start();
  opx = 1; opy = 1;
  cx = 0; cy = 0;
}

 @Override
 public void run() {
   while(running) {
     if(!holder.getSurface().isValid())
       continue; //wait till it becomes valid
     Canvas canvas = holder.lockCanvas();
     canvas.drawARGB(0xff, 0, 0, 0xff);
     canvas.drawCircle(cx, cy, radius, paint);
     holder.unlockCanvasAndPost(canvas);
     update();
   }
 }

 private void update() {
   cx +=opx;
   cy +=opy;
   if(cy>screenheight) {
     opy = -1;
   } else if(cy<0) {
     opy = +1;
   }

   if(cx>screenwidth) {
     opx = -1;
   } else if(cx<0) {
     opx = +1;
   }
 }

 public void pause() {
   running = false;
   boolean retry = true;
   while(retry) {
     try {
       renderThread.join();
       retry = false;
     } catch( InterruptedException e) {
       //retry
     }
   }
 }
}

9 thoughts on “To OpenGL or to SurfaceView

  1. Pingback: SurfaceView threading – when Synchronized isn’t enough | Infinut

  2. Thanks for sharing this article. I want to learn more about tips and tricks you have discovred like Pool-objects and other thinks… Can you help how to start learning that ?

  3. Thanks for sharing!
    This is my first time to develop a game and I googled about android game development thousand times 😦 But I cannot find the answer…

    Would you help me how to decide which is better, View or surfaceview?
    If I wanna develop a game such as Candy Crush without Game Framework
    should i used Surfaceview? or used View?

    PS: Forgive me ! My English is bad, hope you can understand what i mean 🙂

    • If its just drag and drop, then you don’t have to use Surface View, you can use regular views. But, if you plan to add lots of animations, it might be useful. We started out with regular views, and only went to Surface View when we started doing multiple 2d animations.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s