Graphics, Media, and Games • iOS • 50:24
The iOS SDK includes powerful graphics tools for analyzing and optimizing the performance of your OpenGL ES apps. Discover expert techniques you can apply to your apps to maximize their efficiency and attain high frame rates. Learn practical ways to identify bottlenecks, tune performance hot-spots, and overcome any hurdles that could take you off the fast path.
Speakers: Scott Bassett, Filip Iliescu, Benj Lipchak, Seth Sowerby
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
It's 10:30 a.m. and you've just arrived. Coffee in hand for what you thought was going to be a normal day at work. As it turns out, your company has just acquired a work-in-progress iPad title called Touch Fighter 2. Looks like an interesting project, only you've been tapped to give the CEO a demo in just one hour.
No problem, you say. You take a look at the marketing collateral, and this is what the game is supposed to look like. But when you launch it, it looks like this. Not quite right. And what you thought was a splash screen is actually the game running at slideshow speeds.
You put down your coffee, launch Xcode, and get to work. So to get your game back in business, we're going to tell you about two tools that are already available as part of Xcode 4: OpenGL ES Performance Detective and OpenGL ES Analyzer, part of Instruments. And for those of you who signed up for WWDC 2011 in the first two hours, we're going to throw in a free preview of the tool we're working on now, Xcode's OpenGL ES Debugger. So the first tool I'll tell you about is OpenGL ES Performance Detective. And this tool basically looks at the CPU and GPU activity for a particular frame of rendering and tells you why your frame rate is slow.
Unless that is that your frame rate is slow for some non-graphics reason, in which case we'll point you at other tools to help diagnose your problem. So this is the icon for OpenGL ES Performance Detective, in case you happen to cross it in Finder. But we think you'll prefer to launch it directly from within Xcode.
So this is the Scheme Editor. If you go to the Product Edit Schemes menu item and choose the Profile Scheme, by default it will launch instruments and ask you which instruments template you want to load at launch. Alternatively, you can pick one of the specific templates to load automatically, or at the bottom of the list you'll see OpenGL ES Performance Detective. So that will allow you to launch it directly by hitting Command-I or by hitting Option-Run and choosing Profile. Then you can launch your app in your favorite profiling tool.
So we designed OpenGL ES Performance Detective to be extremely approachable to even the most timid graphics developers. Essentially, if you know the name of your own app, you're pre-qualified to use this tool. And the first step is to select your device. So if you're the kind of developer who has one of every device attached to your Mac, here's where you choose which one you want to profile on.
Step two, you need to find your app in the list. Now, both of these steps actually go away if you launch directly from Xcode, where Xcode already knows what your app is and which device you're launching it on. So that brings us to step three, which is to trigger a frame.
So chances are you're not interested in increasing the frame rate of your splash screens or your menus, so you're going to need to get past all that to the meat of your rendering. And at that point, you take a look at the frames per second meter we show you. And when you find the place where you think you have a performance issue, you can hit the Collect Evidence button.
At this step, you can set down your device, have a couple sips of your coffee, remind yourself why you love your job, and just witness the magic. At this point, the tool has taken over your device and is working its magic. And finally, you reap the rewards as OpenGL ES Performance Detective reveals the outcome of its investigation.
So if you believe in magic, you can just cover your ears for the next few slides. The rest of you will be interested to know that the performance detective is essentially just running the same basic steps that an experienced graphics developer would go through to help isolate their own bottlenecks.
So here we have a visual depiction of a graphics pipeline, and what you do to isolate a bottleneck is essentially stub out different stages of the pipeline and see if your frame rate peaks. So here frame rate is basically represented by water, and we have actual pipes representing the pipeline stages. And so this is kind of a normal configuration.
Here we see the pipes have enlarged in size. So if we were to stub out the entire graphics pipeline, effectively giving you an infinitely fast OpenGL ES implementation, and you notice that the frame rate doesn't spike at all, that's a clear indication that your bottleneck is elsewhere. earlier before the graphics pipeline.
If, however, we go back to normal-sized pipes, except for the fragment shader, if we stub out the fragment shader, essentially taking your many instruction shader and collapsing it down to a single fragment shader instruction, and the frame rate does spike up, there's a good chance we can reasonably conclude that that's where your performance bottleneck is.
So the way this works, we capture a frame of OpenGL ES rendering commands, and that allows us to do apples-to-apples comparisons as we play back these commands with slight tweaks each time to perform different experiments. And so why this is interesting, both to graphics novices as well as graphics gurus, is that it saves you time. What might take you hours to do manually, OpenGL ES Performance Detective can do in less than a minute.
So I talked about triggering the capture just by hitting the collect evidence button, but we have another way. We have a programmatic approach to triggering your captures. It's an Apple debug marker extension, new in iOS 5. And this allows you to trigger on whatever you like. You could keep a frame counter within your app and trigger on a particular frame. You could add code to your app to detect the frame per second and trigger on the drop in frames per second. Loading a new level in your game, the phase of the moon, whatever you want, it's under your full control here.
So what sort of results might you get out of this tool? So performance detective might tell you your frame rate's already good enough, in which case we'll even go so far as to say just considering dialing up your graphics workload. And really, you've got to think about your job not just as eliminating bottlenecks, but balancing the overall workload throughout the pipeline. But don't just base your decisions on how fast your iPad 2 or your iPhone 4 are running. You may need to make different decisions within your app in order to achieve acceptable frame rates on earlier iOS devices.
So the second kind of result you might get is it might tell you that your performance, your frame rate isn't where it needs to be, but it's not OpenGL ES's fault. It's not your usage of OpenGL ES that's causing the frame rate degradation. In this case, Performance Detective isn't the tool for the job. It's not the right tool, but if you load up instruments, it's got all kinds of built-in tools to help you diagnose other performance problems on the system.
And the third kind of result you can get is that, yes, your frame rate's not fast enough, and it is your usage of OpenGL ES that's the problem. So in this case, this is really what the tool is made for. This is where it gets interesting. We're going to tell you about what we think your bottleneck is.
We're going to give you some tips for resolving it. And once you've internalized that, once you've taken countermeasures in your code to mitigate the bottleneck, that's when you want to come back into the tool, rerun it, confirm that you've fixed your bottleneck, and see if another one has popped up to take its place.
So at this point, I'd like to demonstrate OpenGL ES Performance Detective and see if we can't make some progress on Touch Fighter 2. Here we have the demo machine, and I am launching OpenGL ES Performance Detective down here. I'm going to zoom in a little bit for you.
Open the new case. We already have our device selected. We have our app selected. Go ahead Click Open Case. At this point, it has launched on the device. Take a look over there. So you can see we're running at low frame rate. I'm going to switch back to the demo machine where we're going to trigger the capture. It seems to be fluctuating around the low 10 frames per second, 9 frames per second. I'm going to click Collect Evidence. And at this point, the tool is working its magic. So I'll switch back to the iPad so you can see what that looks like.
Very simply, the performance detective icon on the device. This is your indication that you don't need to do anything at this point. The tool has quit your app for you. And at this point, the first thing it does is looks at the CPU and GPU utilization to make an initial determination whether you're CPU-bound or GPU-bound. It will capture a frame of your OpenGL ES rendering commands.
And if it decides that you are GPU-bound, at that point it starts running all the experiments. It's taking that frame's worth of OpenGL ES rendering commands, changing it, slightly modifying each time, running it back, seeing how long it took to run it to get a good feel for which stage is the one that's your performance bottleneck.
I'm going to switch back to the demo machine, concluding the investigation. Can take about a minute. It depends on how complex your frame is. Here we can see the outcome of the investigation. . So in this case, it's telling us that we are bottlenecked by our usage of OpenGL ES.
It's pointing the blame at fragment shading. So it's telling us in order to reduce our fragment processing, we may consider reducing the number of instructions in our fragment shader. We can do that by using a simpler algorithm to try to achieve the same effect, hoisting some of the workload out of the fragment shader into the vertex shader. Or using lower precision variables to store our data. Instead of using high P in our shaders, consider using medium P or low P. So armed with this information, I'm going to go into Xcode.
We're going to take a look at our shaders. And I see the first one on the list is a tone mapping fragment shader, so let's take a look at that. Sure enough, it is a giant shader doing a lot of math to achieve what could otherwise be a simpler effect. So we're doing tone mapping to get the colors that the artists of the game were interested in. And whichever developer was working on this looks like they're halfway through switching to a texture-based approach, conveniently.
So in this case, they're baking all of this work into a 2D texture asset. So let's go ahead and turn that on. And there's one other place in the code I need to turn it on. I'm going to rebuild and switch over to the device. Let's take a look.
All right, our frame rate is up around 40 frames, close to 40 frames a second there. So we have made progress. Obviously, there's still many rendering defects to deal with. So let's move on to the next tool. I'm going to switch back to slides. The next tool I'd like to talk about is OpenGL ES Analyzer for Instruments.
So whereas the last tool, the Performance Detective, required very little user interaction from you, you just point it at your app, you point it at a frame of interest, and it takes over and comes back with an answer for you, this tool you can dig a lot deeper into the usage of OpenGL ES by your app.
So you may be -- if you're familiar with instruments, you're probably expecting that we would have graphs on a timeline. And yes, we have all that. But the real power of the analyzer instrument is that it's going to look at your OpenGL ES command stream and data mine it for inefficiencies and misuses of OpenGL ES.
So one way to launch it is just the OpenGL ES analysis template when you start up instruments. Otherwise, if you already have an instrument session going, you can just drag it from your library, OpenGL ES Analyzer, right onto your workspace. Or finally, like we did with Performance Detective, you simply set it up as your profile scheme target within Xcode.
So what do we have in our graphs? By default, we are going to show you a bunch of different per-frame statistics. So for each frame, we'll tell you the number of triangles rendered. We'll tell you the number of draw batches you had. We're going to tell you the total number of OpenGL ES and Eagle calls in that frame.
And finally, an interesting one, we're going to show you the number of redundant OpenGL ES commands in that frame. So that's where you've set OpenGL ES state to something it was already set to, essentially wasting your time. And you can see that the OpenGL ES driver instrument is also loaded as part of this template. And by default, that's going to show you the frames per second.
So if you're running this tool, you're probably curious where your time is being spent on the CPU as far as OpenGL ES is concerned. So we provide you this API statistics view, which shows you all of the OpenGL ES commands you're calling, and it's showing you the number of times you've called them during this trace session.
It's going to tell you the average time spent each time going into this call. And then combine those two together for a cumulative time, the total CPU footprint for each of these commands. And so there's some things you can expect to be near the top. Present render buffer, that's where we're copying the final frame of rendering to the compositing engine.
And then the gl draw elements and gl draw arrays commands, that's where we're taking all of the previously set state, finally getting around to processing it, and kicking off work on the GPU. So you can expect those to be near the top. But if you see something suspicious, like just a random gl state setting command near the top of the list, chances are you're calling it more frequently than you expect. and you should look into that.
Another useful view is this call tree view, which is common to multiple instruments, including the time profiler. You may be familiar with this one. And that's going to show you all the different places you call each GL command from to get a breakdown of where in your app you're calling these things.
TraceView is a great one. If you've got code throughout your app all making OpenGL ES calls, it may be hard to get a grip on what all the calls are and what order you're calling them. So here in this view, you can see them all lined up and in order. And to go along with that, we have a frame navigator. So this will essentially set the bounds of the timeline filter to correspond to a particular frame boundary. And you can advance through these to look at all the different ES commands you're calling for each frame.
So on a frame-by-frame basis, you can see what's different in your call trace. The other useful thing about this is you may have completely non-graphics-related instruments loaded up in your workspace here. And it's going to set the timeline filter for all of them to correspond to this frame of rendering. So you can see what's going on in other instruments corresponding a particular frame. So you say, Benj, I love this OpenGL ES analyzer. It's amazing. But is there anything else it can do? I'm glad you asked.
The expert view is by far my favorite. I think it's the most useful. It gives you a laundry list of all the ways that your app is using OpenGL ES in a way that's either incorrect, according to the API, or just suboptimal for our iOS platforms. So it ranks them by severity. You'll see the highest severity items in red at the top of the list.
Every one of these is something that you should fully investigate, resolve, fix it in your code, wipe it off this list. You never want to have red. The medium severity items in orange, the vast majority of these are legitimate issues that you're going to want to investigate and understand.
Some of them you may be okay with. They might be acceptable to you. But you should really look into all of these. So how do you know what to do when you see one? Well, the little arrow next to each one, you click on that, it's going to open up the extended view. And from there, you can get more information about what the issue was and tips to resolve it. And we'll even show you the lines in your OpenGL ES command trace that we're blaming for the issue.
Maybe it's just one command you call that's problematic. Or maybe there's a sequence of commands spread out over time. We're going to highlight all of them for you. And each one of these, you can look at the call stack, link right back to your code in Xcode to fix the problem. So at this point, I'd like to hand it over to Scott Bassett, a colleague of mine, who's going to demonstrate this tool for you.
Hi. I don't think this is working. Hi. Good afternoon, everyone. As Benj stated, I'm Scott Bassett, and I'm now going to demonstrate the OpenGL ES analyzers. So let's get started. Let me switch over to -- The app. So when you first launch instruments, a window appears and a sheet comes in front of it that gives you a list of all the instruments that you can choose from. The OpenGL ES Analyzer is under iOS graphics, so let's go and select that now.
After selecting it, you'll see it appear on the left-hand side along with OpenGL ES driver, which always gets loaded. I'll point to it so everyone can see. It's really easy to use our tool. All you need to do is go up to the pop-up menu where it says "Choose Target" and Choose target again and then choose your apps. The apps are listed in alphabetical order with the most recent at the top. However, we only have one, so let's just select that now. Then all you need to do is click the record button.
So it'll start the app. I'll switch over to it so you guys can see. And it's basically where Benj left off. However, the frame rate's a little bit lower because the analyzer needs to do some processing on the device to manage the stream. So we'll switch back to the device now that you've seen that.
So after you've run your app for a little while, all you need to do is press stop. And the analyzer takes a little bit more time, so it needs to spend more time processing the functions. You can see that down here. It says estimated trace analysts remaining. And we have like 60,000. It doesn't take too long, though, so it should be pretty quick.
Okay, it's finished now. As Benj stated, the best place to start is in the expert view, which takes up the bottom right-hand side of this window. Let me zoom in so you guys can see it better. So in this view, there's five columns. The first is severity, and there's two levels of severity. There's red square and orange triangle. Red square are really important and should be taken care of as soon as possible.
The next is total occurrences. This is how many times it occurred while the app was recording. The next is unique occurrences. This is how many different places in the code that we found this issue. And the final two are category and summary, which give you a basic idea of what the error is. Let's zoom out real quick.
A good thing to do is bring up the extended detail view on the right-hand side. So I'll bring that up now. All you need to do to do this is go up to where it says view and click the third button next to it. This gives you a recommendation and also the stack trace of where to go in the code. So let's look into the red square issue right now.
So I'm going to click on Validation Error and click on the arrow. It's going to say we're calling GL Active Texture GL False, and that's not correct. We can see up here it says in the recommendation -- let me zoom in real quick -- it says GL Active Texture GL False, Texture Unit Out of Range. So that doesn't sound good. And we can look down here and see the stack trace. By double-clicking on the stack trace, it will bring it into view in the main area.
And we can see the code that we have. And right here we can actually see GL texture, active texture, GL false. However, we can't edit in here. But there is a good thing, though. If you look up on the menu for this source code viewer, there's a little Xcode button right up here. It might be pretty hard to see for everybody. So if we click on that, it'll bring us to the place in the code where this issue is actually happening.
So we can see right here we're calling GL active texture with GL false, but it probably should be like GL texture zero. So let's just try that. GL texture zero. Okay, so that should take care of that issue. So let's switch back to Xcode and look at another issue. Or instruments, excuse me. To go back to all the issues that are happening in your app, all you need to do is click on expert and then click on it one more time.
Let's take a look at one of these orange triangle issues. While there's a lot listed here, I've decided to look at recommend using VBO because I've seen big performance improvements by taking care of this one. So if we look in on the right-hand side in the extended detail view, it says, your application sourced vertex array data from client memory rather than from a vertex buffer object. In this situation, every time GL draw arrays or draw elements is called, the data is retransmitted to the graphics hardware to be rendered. That doesn't sound good at all, so let's zoom out real quick.
And let's click on the arrow to continue further in. And you can see there's many places in the code where this is happening. And we can actually see different stack traces on the right-hand side over here by clicking on different ones. So let's take a look at one of these now. So I decided to look at this unsigned byte one. It's most likely color. And we can move to the right-hand side and click on Planet Render Object at Time.
And if we look at the code right here, there's a -- fix me, it says insert VBO code here. I guess they didn't have time to do that. And also there's a vertex attriter pointer. It's a normal one, except at the end they're passing in CPU data instead of, like, using a vertex buffer object. So let's change the code to do this now.
So, again, we'll just click on the Xcode button to go to the location in code. And it's really easy to use vertex buffer objects. All we need to do is create the VBO. And if we look up here, this looks like -- let me see. Wait a second. This is the init function. So this would be a good place to put it. And there's actually a fix me in here to do that.
I've created a code snippet to do it already. So let's just add it in here. It's really easy to create a VBO. All we need to do is call genbuffers and then bind the buffer and then just upload the data into this buffer. That's all you need to do for that.
And then down below where we're actually going to render the object, I created another code snippet for that. And if we look at the difference between the two, I'll just comment out this one so it's easier to see the difference. Basically all we need to do is call bind buffer with the buffer that we created before, and then instead of passing in the CPU data, we'll just pass in an offset.
But since it needs to go in as a pointer, we'll pass a null for this. So now we've made these two changes. So let's see what this did to the app and see if we got any improvement or fixed any issues. So I'm going to run the app and switch back to the app now.
Okay, so now look, the ship's showing up properly, it looks like, and we're getting around 55 frames per second, which is a big jump from what we were getting before around 40. However, there's still some weirdness happening, like that planet just changed and some things still don't look right. So with that in mind, I'm going to introduce Seth Sowerby, who's going to go over our newest tool. And thank you.
Hi, I'm Seth, and I'm a manager in the GPU Developer Technologies Group. Today I'm going to introduce you to a revolutionary new way to debug your OpenGL ES apps on iOS 5 visually, all without leaving the comfort of Xcode. The OpenGL ES debugger is a brand new part of Xcode 4.2 that allows you to capture a frame of your app's OpenGL ES activity, then step through the draw calls, seeing how it builds your frame, inspecting the state and objects at any point. Why would you want to do that? Well, maybe you've got a rendering defect and you want to quickly hone in on where the issue is. Or perhaps you want to get a better view of your per-frame workload by looking at all the draw calls that you're doing.
Sounds great, Seth, I hear you say. Or at least think to yourself. Where can I find it? Well, the OpenGL ES debugger is built right into Xcode 4.2. Integral part of it. Nothing extra to install, nothing to enable, no switches to change. Right there. If you're an OpenGL ES app running on an iOS 5 device, you'll see this new button on the debug bar. Simply click that.
It will capture a frame of your jail usage, pause your application, background it so it's safe, and launch the debugger. Alternatively, if you prefer, you can add a breakpoint at any point in your code and use the new Capture OpenGL Frame breakpoint action to capture wherever you want. Or you can use the code marker that Benj talked about earlier.
So the first thing you'll see once Xcode has finished capturing your frame is the main frame-buffer view in place of the main Xcode editor. In this, we show you the contents of your currently attached frame buffer at any call in your app. And in this case, you can see we also highlight the last draw call as a lurid green wireframe, so you can easily pick it out.
In this app, as you can see, there's a color buffer and a depth buffer attached. And for depth buffer values, we detect the range of depth buffer values you use so that we can map that to a gray scale, and you can see in detail which area of the depth range you're using.
The frame scrubber allows you to quickly move back and forth through your frame, quickly finding the draw call that's drawing a particular rendering artifact or some other part of your frame. As you move to the left, you move back to the start of your frame where you're probably clearing your buffers and rendering your skybox, perhaps.
If you move all the way to the right, you'll see your final frame ready to present onto the device screen. You can also navigate the frame using the jump bar, and it also gives you a handy description of what exactly are the parameters to that currently selected draw call.
In the debug navigator, where you're used to seeing stacks and queues from the CPU debugger, we'll show you the full command list of all the OpenGL commands you're making within that frame. And you'll see that we have different icons for draw calls and for state calls. So you can instantly quickly see where you're doing draws, where you're doing state calls, and are you doing a ton of state calls, and then one draw call, and then a ton of state calls? Quickly let you see that you may be doing something that's not very performant.
Also, if you disclose on any of the API calls, you'll see that we give you a full stack frame for that point in your frame. So at any point where you found a rendering defect, disclose it, see the stack, click on anything in the stack, go back to your source code and fix the issue. You don't have to start delving back to see, okay, well, I know now it was a draw call where something went wrong. You can jump straight to it.
But a long list of draw calls and state calls. It's kind of flat. It's kind of hard to see what's going on in your app. Really, there should be some better way of kind of seeing what's going on. So with that in mind, we introduced a new OpenGL extension into iOS 5, Apple Debug Marker.
This lets you use simple text markers to annotate the hierarchy of your rendering. So you can label when you're doing your pre-pass or when you're doing the lights at the end or when you're drawing a structure or drawing cars, whatever you want. The choice is up to you. It's you annotating your code so that you can read it. And see what's going on. And you can nest these markers as deeply as you want so you can reflect the full complexity of your rendering loop.
To use the Apple Debug Marker? Really simple. Just call push_debug_marker before the first call you wish to annotate, passing the text label you want. and Karl Popmarker when you're done. Simple as that. We support this new extension out of the box with the GL Frame Debugger, and we'll also be introducing it to our other GL debugging tools.
In the debug area, we've taken the variables view that you've become familiar with for viewing your autos and your variables and your console log and all that type of stuff, and we've extended it to show your GL state. So we have viewers for your GL context state, your bound GL object state, and all the state for all the objects.
To make it easy to scan the great wealth of GL state that there is, we group these semantically. So we put all the values that are related to blending together, all the values related to depth test together, so you can see what you want to see related to each type of operation. We also add summaries to these. So you can just see that if blending's turned off, you don't need to see all the blending values. If blending is on, it will show you, okay, blending and blending this factor and that, so you can quickly scan what's going on.
Similar to how the CPU debugger will highlight variables that have changed as you step through your code, we'll show you state that has changed since the last draw call. So as you step through your frame, you can see your state vector getting updated from draw call to draw call. If suddenly you're changing a state value that you didn't expect to change, you'll see it highlighted in blue.
Oh, I've turned my depth test off. Why has that happened? That's wrong. Jump straight to the issue. In the bound GL object view, we'll show you, unsurprisingly, the state for all your bound GL objects, including such goodness as your uniforms for programs or the filtering levels on each texture, everything there that you need to find.
In the Object View, we show you all the objects that are currently in your context. Two options there. You can have the BoundGL Object View, which shows you just those objects that are currently bound, or the All Objects will let you search through any of them. And we also have filters there where you can select whether to view all of them or just by type, such as texture or shader. The Bound case is the one we show by default because that's really great for any draw call, showing you which objects, which textures, which programs you're actually drawing with.
In the case of textures and render buffers, as you can see, we'll show you a thumbnail of their contents. Makes it really easy to tell them apart. It's not so easy to do with vertex buffers and programs because, you know, how do you thumbnail a vertex? And it's kind of hard to tell GL objects apart by name only when GL's concept of a name is an unsigned integer.
Which leads us to the second new extension we're introducing to OpenGL in iOS 5 for debugging purposes: Apple Debug Label. This lets you attach a simple text label to any GL object you've created. It makes it quickly -- you can quickly, easily look and see, oh, that's texture2.pvr, or this is the mesh for my castle vertices, or whatever. Again, the choice of how you label them is down to you.
To label your objects, simply once you've created your object, just call label object. Just pass in what the type of the object is, the name, and the text you wish to label it with. There's also an API for querying these labels back. So if you want to use it for building your own login code within your app or some offline tool that you have, you can do that, too.
If you double-click on any of these objects, you'll get a detailed view so you can hone in on seeing exactly what's in there. And, yeah, we provide the functionality you need to view them in detail. So if it's textures, you can select the MIP level or the cube face. If it's a program or shader, you can check the compile info log. So you can see all you need to figure out what's going on.
For vertex array objects, we even unpack all the vertex data from the vertex arrays and element buffers and array buffers that are attached to the VAO to provide you with a list of vertices as your currently bound vertex program will see them. So you can see exactly what data it's going to be operating on.
Because we build all these object viewers on top of the existing assistance editors within Xcode, you can configure them to suit your workload and have multiple windows open. So you can have one, in this case, we show you bound GL objects. And we also, in this case, showing you another new assistant we've brought in specifically to make use of this variability in that you can have one that shows you the source code at the point you're calling GL.
So you can just step through your code or step through the draw calls or API calls in your frame, seeing the buffers update, seeing the bound objects update, and seeing exactly where you are in your code at that same point. As soon as you spot an issue, you're right there. Just jump to your code and fix it. Okay, at this point, I'd like to invite Filip Iliescu to come forward and demonstrate the OpenGL ES debugger as he fixes the final few issues in TouchFighter 2. Thanks, Seth.
All right, T-minus 20 minutes to executive demo, and I'm really excited. So I'm going to show you guys our new OpenGL ES debugger with NX code 4.2 by fixing some of the issues that we still see in Touch Fighter. Switching over to Xcode here. I'm going to start out by looking at what we believe to be the correct rendering that we're expecting out of Touch Fighter. And then I'll go ahead and compile and launch Touch Fighter on this iPad.
We can see what it's actually rendering like. Okay, yeah, yeah, it looks pretty broken. I can still see some issues here going on with that mothership. There's something wrong with the geometry. And the fighters are actually flying behind the planet, the background. That's kind of weird. The planet actually looks completely broken. Looks like there's -- the texture's wrong, and there's some kind of big ring around it. When I was expecting a glow. So I'll go ahead and take a capture of this guy. And switch back to Xcode here.
Now, since I can basically see all the rendering defects while the game is running in idle and I don't really have to interact with it, I'm going to go ahead and use this capture button right here that shows up if your app is linking to OpenGL ES to trigger a capture.
And while this is capturing, I just want to note that you can also use breakpoint actions or you could actually use the new API that Seth and Benj mentioned in their slides to trigger a capture programmatically. So this is going to take a second to kind of grab the frame and all of the API calls, and once it's done, it'll show up in Xcode's main editor here. And the first thing you'll see is the frame that you actually were capturing.
Okay. Well, here we go. So first off, I just want to note that this is the main render buffer here, the main color render buffer. And you can actually see what buffers you have enabled. You can show color depth or stencil. Or my favorite is to just set it on auto because if you set it on auto, then it will just show you the current attachments that you have on the current FBO, the current draw FBO.
And right above that is Xcode's editor jump bar, which will always show you the current trace location for all of the objects and state and everything that you're inspecting. Over on the left, we have the GL frame navigator. And this might be a little hard to see, but I'll zoom in on it for you guys. And essentially, this gives you the complete list of all of your API calls in the order that you made them. You can further disclose that to actually get a backtrace to your code.
Now, you can also see these folders that we've set up here. And the way you set these up is really simple. You just use the new GL Push pot marker API that Seth mentioned in his slides. You can further open up one of these markers and you get the complete list of API calls that you surrounded using those markers.
Down below, we have the debug area, Xcode's debug area, which we've added our state inspector. And the state that you're seeing here is based on the current trace selection, which you can always note, once again, by just looking at the jump bar and the API item in the jump bar. This is the state at that point in the API trace.
And over on the right, we have Xcode's assistant editor, which, essentially, if it's not open, you can always trigger the show assistant editor button here to open it. And we have three different categories here that you can choose from: bound GL objects, all GL objects, and the stack. Now, the stack is kind of nice. What I'm going to do is actually go ahead and open up a new assistant down below and select stack.
Now, the nice thing about this is it will show you the -- it will show you the GL API exactly at the point where you're inspecting. So if, for example, you want to actually -- if you're going through this and you're selecting various points in the API, it will actually show you where you made that GL call.
If you want to actually show something in the debug navigator to see where it falls in the order, you can right-click on the main editor and click reveal in debug navigator, and it will open up over here in the debug navigator, further disclose and get a stack frame.
Great. Well, I have this set up now, exactly how I would go about debugging. So let's get started. First off, I want to look at what's going on with this mother ship. It's kind of interesting. The geometry doesn't look right. According to my picture that I had, it looks a lot different.
So I think I'm just going to go ahead and figure out how it's being drawn by dragging the knob of the slider here, the scrubber, to show every -- to show the rendering -- intermediate rendering calls that leads up to that. Now, I'm going backwards, and while I'm getting there, I just want to note one thing here.
Two things, actually. The first thing is you'll notice this green wireframe, and that basically tells you what the geometry is that you're currently rendering with that draw elements call or what have you. You can always right-click to hide or reveal the wireframe at any point if it's not showing, for example. So the other thing I want to note is that you actually can see all of the intermediate rendering happening on the device itself.
So there's our mothership. Great. I think I'll just stop there. And then I'll switch back over to Xcode. And then I'm going to right-click to reveal in the debug navigator, I have this big mothership group here. Okay. Well, I'm actually kind of not seeing anything right off the bat.
Maybe if I go to the very first draw where they actually started drawing this mothership, I can see something interesting. Well, the first thing that pops up is in the state viewer down below, anything that's highlighted with blue is any new state that's been set up for this particular API call. So, okay, that's interesting. I have a depth buffer and depth reads enabled.
And I actually don't see a depth buffer, and I can tell right away because I've got it set to auto and all I see is a color buffer. Okay, well, I can also disclose further on the bound frame buffer and notice that there's no depth attachment in it either.
Well, that kind of tells me that maybe I've not set up my depth attachment correctly or something. So I think the first thing I'm going to do is just go over into Xcode's project-wide search and just search for GL depth attachment. And see if I've actually set that guy up. Or set it up wrong. Oh, it does show up in my search. Oh yeah, look at that. It's commented out with a big fix me.
Looks like somebody thought they might need one, but they didn't actually set it up. Okay, well, let's go ahead and recompile this. And see if that fixed anything. I'll go ahead and switch back over to the iPad. See if anything's fixed here. I'm actually -- I'm actually fairly certain that that fixed... Oh yeah, yeah, the mothership looks good now.
But did it fix anything else? Let's see, the fighters are still flying behind the planet, the texture's still busted. All right, let's go ahead and switch back over to Xcode and grab another capture of this. See if we can figure out what's going on with the rest of these defects.
So while you're capturing, you can notice there's this area over here that tells you what's going on. So you always know that the capture is going. And look at that, we have a depth buffer. Yeah, that's kind of what I thought. You can always just view the color if you want or just -- or select both. But let's just keep it on auto. And that should give us what we need. Okay.
Well, Let's take a look at this planet. I'm going to go ahead and put this back on stack because I really like that. I think it's incredibly useful. Always seeing the top of the stack for each call. And I'm going to go ahead and just move over to this planet group that I've created.
And I guess I'll further disclose that because I'm actually getting the ring. I'm more interested -- let's see, this is the first draw call, so I'm guessing -- yep, that's the planet I'm looking for. Great. If I go ahead and -- Zoom in on the state area. Well, notice I have active texture unit one set, and I've got two texture units in my bound GL objects.
Yet, if I look at the bound resources, The Texture Unit 1 is the one that I'm actually interested in drawing with, so that looks correct. Although I am using OpenGL ES 2.0, which means I've probably got some shader going on that's doing my rendering. Let's take a look at the fragment shader.
It looks like I've got -- I'm sampling with this uniform here. Well, okay. If I click and disclose the program, In BoundGL Objects, I can actually view the values of each of those uniforms. And yeah, it looks like I've got -- I'm sampling from texture unit zero. Well, that's not the texture I want. Interesting.
Okay, well, where in my trace here am I actually setting active texture unit one? Oh, it looks like, yeah, right about there. Yeah, there it is. Shows up right in the top of the stack. Well, somebody put a fix me there and didn't fix it. All right, so let's just set texture zero.
And while I'm here, I'm going to go over to the next draw call, which is that ring that was appearing around the planet, and take a look at that. Now, I can see immediately in my state area right here, and I'll zoom in, that somebody enabled blending. Okay.
So that kind of makes sense, although why is it set to zero? That's kind of weird. I would expect that if I'm trying to blend with the background, I should be using something like 1-source-alpha. So where do I actually set that in my draw list here? Oh, yeah, there's the blend funk.
And it looks like, yeah, there it is. Somebody set it to zero. And, of course, put a big fix me, like they thought that was the, you know, the right thing to do but didn't fix it. Great. Well, I'm going to go ahead and recompile this and run it again and see how it looks. And I'll switch back over to the iPad.
Yeah, yeah, it looks like the planet's fixed, and we've got this nice glow around the planet and everything, the way it's supposed to look, but, yeah, those spaceships are still flying behind the planets. Something's really wrong there. All right. Well, I'll go ahead and try and trigger a capture when I have some spaceships in the vicinity that I'm interested in. There we go. That should do it. Great. And see how those get drawn. Well, once again, I've set up my group, so it's pretty easy. I can just click on it. It'll take me to the last draw call in that group.
There's the planet. Okay. Well, I can step through the draw calls, you know, by using the steppers here. Going draw call by draw call. And there's the mothership being drawn. Or I can use the scrubber. Yeah, I know that. But I've also set up these really nice groups.
So why don't I just skip to the last draw call on the mothership. That'll speed things up a little bit. And I can click on the enemies. Okay. So there they are. They're drawing in front of the planet. And there's -- what's going on here? Oh. Looks like somebody's drawn the planet twice.
After they've drawn everything. Okay. Well, that's kind of interesting. Should be able to just see -- yeah, there's the push/pop marker groups, and there's a big fix me, of course. Shouldn't we render the planet before other objects? Well, yeah. Okay. Great. I'm just going to go ahead and comment that out since we're already rendering it somewhere and recompile.
See how it looks. Okay. Well, yeah, of course, that fixed it. Great. Well, yeah, our frame rate's locked at 60 frames per second, and I wonder if that, you know, probably improved a little bit by removing the redundant draw as well. Awesome. Well, that does it. I'm going to hand this back over to Seth to wrap up for us.
Thanks, Phil. So, the demo's saved, the CEO's happy. Time to crack open that magnum of champagne. If there are just three things we can leave in your head from this session, they're these. One, these tools exist. The Performance Detective for helping you quickly find your performance bottlenecks. The Analyzer Instrument for giving you the laundry list of your OpenGL ES misuses and inefficiencies.
And the new OpenGL ES Debugger for helping you dive into your rendering code and fix things quickly and efficiently. Two, where to find these. Simple. Xcode 4. The Frame Debugger is built right into Xcode 4.2. And you can launch the other two tools directly from Xcode. Just hit Command-I and off you go.
Thirdly, please use these tools. They do you no good if you don't use them. And don't wait until something's broken. The analyzer instrument is great for just running it occasionally, seeing if there's stuff broken that you don't know about. Fix your bugs before you see what's going wrong. For more information, a couple of dev evangelists you can contact. Alan Schaffer, who's a graphics and imaging evangelist, and Mike Jurowicz, who's the dev tools evangelist.
Just want to mention a couple more great sessions coming up. Right after this session, there's Best Practices for OpenGL ES Apps in iOS, which gives you some great info on making the best use of GL and getting the best out of the devices. And tomorrow's a great session, Adventures in OpenGL for Mac OS X, Lion, where you'll find out what some of the great new things we're introducing to OpenGL on the desktop in Lion.