Setting OpenGL view for iPhone 4 retina hi resolution

This had me scratching my head for awhile.
At first I thought that glGetRenderbufferParameterivOES would properly detect Retina screen at 960×640 but it keeps returning 480×320.

A little explanation on Retina screen first. Older devices have 320×480 screen resolution. With new iPhone 4 and iPod Touch 4G the screen has 640×960 but on the same physical area. This means that each pixel is 4 times as small.
To properly simulate older games resolutions iOS will replace each of your 320×480 game pixel by 4, this way your game will look identical.

So the API handles this differently. If you developed for iPad you notice that glGetRenderbufferParameterivOES return 1024×768 but not retina displays. Instead you have to check it yourself:

int w = 320;
int h = 480;
 
float ver = [[[UIDevice currentDevice] systemVersion] floatValue];
// You can't detect screen resolutions in pre 3.2 devices, but they are all 320x480
if (ver >= 3.2f)
{
	UIScreen* mainscr = [UIScreen mainScreen];
	w = mainscr.currentMode.size.width;
	h = mainscr.currentMode.size.height;
}
 
if (w == 640 && h == 960) // Retina display detected
{
        // Set contentScale Factor to 2
	self.contentScaleFactor = 2.0;
        // Also set our glLayer contentScale Factor to 2
	CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
	eaglLayer.contentsScale=2; //new line
}

Easy right? Remember, you must do this before calling glGetRenderbufferParameterivOES.
For touch positions just multiply each position by the scale factor.

I’ve only tested this on the simulator since I don’t own an iPhone 4 but it should work and detect either you are on a regular or retina screen.

16 Comments

  1. roy

    Hey David,

    Just browsing your articles and I wanted to let you know that the ‘drop shadow’ on source-code text makes the code snippets completely unreadable, sorry :(. Good articles though!

  2. Kurian

    Thanks, this information was IMPOSSIBLE to find anywhere.

    • Kurian

      Also, glViewPort(….) needs the new width and height. self.bounds still returns 320×480 for width and height.

      • Tim

        I get the same problem as Kurian, regardless of scale factor – self.bounds still returns 320×480. Is this expected? Do I need to multiply self.bounds by the scale factor – or have I not set the scale factor correctly in the first place?

  3. Bart

    As Kurian says, it was hard to find this valuable code!

  4. Thanks! It’s amazing how many opengles games don’t support retina correctly. My game (zbot 3d) looks twice as good now.

  5. edgar

    hi i just have a interesting cuestion
    i have i iphone 4 and games are very slow because it have the same gpu
    and i wish to run all my games in low res 320*480 for more game performance
    can somebuddy tell me how can i do it or adit ipsw files or with cydia or something
    just let me know ill understand it
    thanks

  6. Andy

    You’ll need to edit the binaries, disassemble them back into code – find the location where the particular call is being made. You will need to find the signature of the selector call to [UIScreen mainScreen]. Assuming the game is using a similar method to the one above, you should be able to short circuit the if comparator. Depending on the structure around the comparators, it should also be possible to fix the float to a predefined value instead of calling [[[UIDevice currentDevice] systemVersion] floatValue]. If you can force the float value of 3.2 in there, it will most likely have the same effect.

    This seems like a horribly large amount of work to do though, it’s probably easier to just wait out the iPhone 5, and then most of the games should be silky smooth by then. ๐Ÿ˜€

    Thanks btw David – really nice little snippet of code.

  7. orion

    great post.

    fwiw, using the boilerplate code generated by the Xcode 4 “openGl” template app, my implementation was to add the following to the end of MyAppViewController.m / awakeFromNib():

    i hope blog code formatting doesn’t hose it.

    [code]
    UIScreen* mainScreen = [UIScreen mainScreen];

    int w = 320;
    int h = 480;

    float iosVer = [[[UIDevice currentDevice] systemVersion] floatValue];
    if (iosVer >= 3.2f)
    {
    w = mainScreen.currentMode.size.width;
    h = mainScreen.currentMode.size.height;
    }

    if (w == 640 && h == 960)
    {
    // retina
    self.view.contentScaleFactor = 2.0f;
    self.view.layer.contentsScale = 2.0f;
    }
    [code]

  8. chris

    Thanks very much, that worked a treat!

  9. nminhtai

    It works like a charm!
    I can’t find the solution anywhere but here.
    Thank you, David.

  10. w3pm

    FYI this code can be simplified to:

    float scale = [UIScreen mainScreen].scale;
    self.view.contentScaleFactor = scale;
    self.view.layer.contentsScale = scale;

  11. Neil

    Thank you very much! This helped save the day on one of our projects!

  12. Thank you very much! ๐Ÿ™‚ I was trying to change the XIB file and I was getting crazy… You saved my day ๐Ÿ™‚

  13. That worked well for me too ๐Ÿ™‚

    Three lines of code to do something that Apple seem unable to explain sensibly.

Leave a Reply