When it comes to rendering, speed is everything. Well, speed and looks. I mean, you need looks. No sense in drawing things if they look terrible. So the two most important things are speed and looks. And latency. Obviously latency is important. You can’t bloody well play a game if it takes several seconds for your input to make something happen on screen because the engine is building up these massive framebuffer effects. So the three most important things are speed, looks, and latency. And compatibility. What’s the sense in writing an engine that’s only fast and pretty and on one set of hardware? That’s buying into the pointless wanking and pissing matches between hardware manufacturers, right there. So our top priorities are speed, looks, latency, and compatibility. And consistency.
Let me start over.
In your typical game, you’re generally pushing to render stuff as fast as you can. That’s still the case, obviously. But if you’re developing for VRWhich we aren’t, but for educational purposes we’re pretending we are. then you have something new to worry about: StutteringAlso known as hitching, stalling, frame-skipping, frame-dropping, and “crap”.. In the old days, having the game hiccup and pause for a tenth of a second was nothing more than a mild annoyance. Maybe the player rounds a corner and suddenly gets line-of-sight to (say) the Doomsday Cannon. But maybe the model or texture isn’t in memory yet, or they haven’t been properly packed up and made available to the GPU. So the rendering stalls for a split second while it gets the assets ready to be drawn.
Now that the cannon is in memory, it’s not a problem any more. The player can play peek-a-boo with it all day and they won’t bump into any more stutteringUnless we had to dump something ELSE out of memory to make room for the cannon. In that case, the player might have another stutter when they go look at that OTHER thing.. A bit of stuttering is a harmless nuisance. We work to prevent it, but no engine is perfect.
But in the world of VR, stuttering is no longer a nuisance. It’s a dangerous failure state. When the screens are strapped to your eyeballs and you’re moving you’re head around, having rendering skip some frames – even just one or two – can be jarring, disorienting, and even nauseating. Anything that causes physical discomfort to the user needs to be at the top of the priority list. Screw special effects. Screw graphics. We can’t afford to miss a frame. If that means the Doomsday Cannon spends half a second untextured, or unlit, or even completely missing, then so be it. As long as we keep drawing the scene.
This presents a scary problem. What if we just can’t keep up? What if we’re stuttering not because we’re waiting to cache some assets, but because the hardware literally can’t draw eveything in the given time? What if there is suddenly just too much stuff to draw? If someone is running the game on hardware that just barely meets the requirements, you might run into a scenario where some arrangement of objects pushes us over the fatal limit that will overburden the GPU, stutter, and send the user to puke city.
This becomes important when you’re dealing with huge numbers of cheap objects, like grass. In that case our worst-case scenario is one where the player is standing in the middle of a field of grass. For our hypothetical game, let’s say that this scenario is unlikely but not impossible. Which means that the engine needs to be able to handle this state gracefully.
You could make the case that it makes more sense to simply render this worst-case scenario every frame rather than allow it to surprise us. It needs to be able to handle maximum grass, so why waste precious CPU time trying to minimize it? If we need to be able to handle the worst-case anyway, then perhaps it’s safer to just make sure we’re always drawing the worst case? Once we get the game running at the desired speed, we can be confident that no matter what happens or what we need to draw, it shouldn’t harm our framerate or our poor user.
Of course, the counter-argument here is that we might want to cull grass because we’ve got mutually exclusive stuff in our world. Maybe we have trees. We need to be able to handle maximum trees. We need to be able to handle maximum grass. But there will never be a situation where you have the maximum of both at the same time. This spot of ground is either trees or grass, but not both. In which case it makes sense to do some kind of culling. Fine. But say you’ve got a few different systems playing off each other: How can you be sure there’s not some magic ratio of the three that will lead to frame skipping? You can’t test everywhere in all configurations.
I honestly don’t know. It might be better to simply apply the maximum load at all times to avoid any surprises, even if that means raising your minimum system requirements. In that case once the user has it running smoothly, they can feel safe that it will keep running smoothly and not oblige them to go mucking about with graphics settings once they have the headset on.
People are still working on this stuff and figuring out what the best practices are. It’s one of the reasons I’m so into VR right now. There’s room to learn new stuff rather than just read a white paper from someone who solved the problem fifteen years ago.
Are we a thousand words into this and still not making polygons? Apologies. I don’t think we’re going to make any polygons today. I think I need to laboriously talk around the problem for fifteen hundred words before I do something radical like write actual code.
Grass is interesting because it’s small. It usually doesn’t occupy much screen space, but if you can crouch (or it can be very tall) then there are situations where it might fill ALL Screen space. It’s usually made up of tens of thousands a really cheap triangles. However, if your game engine is aiming for photorealism then you might want to blend the edges of the grass blades. This looks nice, but can get expensive fast. It’s also strange because it’s almost never the center of attention, yet it has a massive impact on the sceneTry turning off grass in Oblivion. The game will suddenly look like Morrowind..
The problem with grass is that we need it everywhere, but we can’t actually draw it everywhere. We need to draw grass right here at the player’s feet, and we need to not draw grass on those far hills. At this distance we can just paint the hills green and it’s all good. The trick is how we transition between the two. Having grass suddenly pop in is jarring and distracting. Same goes for having it abruptly vanish again.
The standard trick is to have the grass fade in. At (say) twenty meters it’s just barely visible, but by the time you’re five meters away it’s fully opaque. The problem with that idea is that it drags us into the thorny world of alpha blending. (That’s fancy programmer talk for drawing semi-transparent surfaces.) For starters, drawing alpha-blended stuff is slower than drawing non-blended stuff. Second, you have to sort all the semi-transparent stuff you’re going to draw, so that you start with the most distant and draw the nearby stuff last. Otherwise:
Those blades of grass on the right show us what happens if you don’t draw back-to-front. The edges of the blade nicely blended with the background just fine. But then when we’re drawing the more distant grass it gets blocked from view by the wispy edges of the close grass. There’s no way to properly “insert” the far grass in between the near and far stuff. That’s like painting and needing to put some paint down between the canvas and some paint you’ve already applied.
Finally, drawing back-to-front is more expensive because we get more overdrawDrawing over the same pixel again and again. In the ideal scenario, you would draw every pixel on screen exactly once.. If we weren’t trying to blend the edges of the grass above, then it would properly obscure our view of the grass behind it, and the GPU wouldn’t need to waste time putting those pixels down.
So fading grass in looks great, but it sucks. It requires some CPU-sucking sorting, it causes overdraw, and alpha-blended stuff is slower to draw. Also: Aside from speed concerns, sorting is annoying. You’ve got this awful sorting system right in the middle of your beautiful rendering pipeline. It introduces a lot of code complexity. Oh sure, sorting itself is basically a solved problem. But you’ve still got to do it at the right time. You have to do it just often enough to keep the scene from looking glitchy. You need to clutter up your rendering logic with stuff to make objects fade in and out. A couple of lines of code can easily become a couple of pages if you really want to do it right. So what is a cheap, corner-cutting, lo-fi developer to do?
There’s a trick I want to play around with. We’ll try it next time.
 Which we aren’t, but for educational purposes we’re pretending we are.
 Also known as hitching, stalling, frame-skipping, frame-dropping, and “crap”.
 Unless we had to dump something ELSE out of memory to make room for the cannon. In that case, the player might have another stutter when they go look at that OTHER thing.
 Try turning off grass in Oblivion. The game will suddenly look like Morrowind.
 Drawing over the same pixel again and again. In the ideal scenario, you would draw every pixel on screen exactly once.
Push the Button!
Scenes from Half-Life 2:Episode 2, showing Gordon Freeman being a jerk.
Diablo III Retrospective
We were so upset by the server problems and real money auction that we overlooked just how terrible everything else is.
Mass Effect Retrospective
A novel-sized analysis of the Mass Effect series that explains where it all went wrong. Spoiler: It was long before the ending.
This Scene Breaks a Character
Small changes to the animations can have a huge impact on how the audience interprets a scene.
The Best of 2019
I called 2019 "The Year of corporate Dystopia". Here is a list of the games I thought were interesting or worth talking about that year.