Too much stuff on screen

December 28, 2014 | Filed under: gsb2 | programming

This is a screenshot of GSB2 (click to enlarge). Nothing particularly impressive, but when looking at it, and then stepping into code to see whats going on, it’s clear that the engine is kinda pushing against limits for hardware on laptops etc (This is with graphical detail at maximum, so that will be less of an issue eventually).

screenshot_28-12-2014_13-09-31

I think that ultimately, I’m making too many draw calls, and lesser hardware can’t handle it. because of the nature of the engine, those draw calls are vastly higher than the amount of actual objects on screen, because there is depth, and lightmaps and other trickery that magnifies the effect of, for example, just rendering a single sprite of a fighter ship. That fighter ship probably involves more like a dozen draw calls :(. In the scene shown, we have 915 3D objects (most are not onscreen), 266 depth objects, 648 lightmap objects, 89 ‘splats’, 372 effects and 216 saturated effects. Thats clearly a lot :(

I’ve seen the game do 5,000 or more draw calls in one frame, and thats kinda bad, so the way I see it my approach to optimization could take various paths:

1) degrade some less important stuff when we exceed a certain number of calls / drop below 60 FPS. Not ideal, but a brute force way to fix it.

2) Further optimize some stuff that is currently done in single draw calls, like parts of the GUI, to get the general number of calls down.

3) Slot in a layer between my current engine and DirectX, which caches states yada, and collapses draw calls into fewer calls where the texture/shader/render states are the same.

Theoretically 3) is vastly better than the rest, but I fear that I’m adding another ‘layer’ here which could in fact be slower, and also that I’m keeping poorly optimized code and fixing it after the event. After all, the best solutions to speedups are always algorithmic, not close-to-the-metal; tweaking. However, another benefit of 3) would be that such an abstraction layer makes the job of porting the game slightly easier. I’m considering implementing it anyway, so I can at least see how often such a ‘collapse’ of draw calls can happen. In other words, would this reduce the count by 5% or 90%?

So that means replacing the DrawPrimitve() calls with a macro, maintaining a cache of the render states (or maybe just letting any RS change flush the buffer? and just (for now) initially keeping track of the collapsible draw calls. I’m going to give that a go… Or maybe I should see what the hell all those objects are first…

12 Responses to “Too much stuff on screen”

  1. Alfie275 says:

    Presumably you ought to be able to draw any identical things all in one draw call, since they’ll all use the same textures. You could pack all the ship textures into one texture, either using array textures or an atlas.
    To avoid changing shader uniforms all the time and if you don’t want to transform stuff on cpu you can just buffer the parameters and pass them to the gpu (either array buffers or textures) then when you render use glDrawArraysInstanced or glDrawElementsInstanced and in the vertex shader lookup the transform parameters in the array/texture using gl_InstanceID.

  2. cliffski says:

    I’m using directx :D, and the ships aren’t simple textures, but horrendously complicated multilayered objects which may have lightmaps inter-leavedwithin each layer :(

  3. Alfie275 says:

    Ah sorry, just assumed indie dev = openGL ;)
    Anyway, the basic idea should still work: Bunch together everything that uses the same shader/textures and draw them all in one call. There may be some slight overhead in keeping track of this, but it is faster than if you keep switching textures/shaders.

  4. cliffski says:

    The trouble is that the texture sizes for everything are non trivial, there is a lot of stuff going on, plus generally the scene is drawn back to front (apart from some visual effects). I am wondering about changing some of this, but the current system gives me (free) really nice aliasing and blending on stuff like the corners of ships, which looks really bad if its pixel-clamped like a 3d game when drawing it all out of order.

  5. c0d3rguy says:

    You should definitely consider ‘batching’, but keep in mind that it’s only going to be feasible if you can guarantee that you’ll render a bunch of ‘objects’ having the same state, like let’s say all enemy ships will be rendered in the same ‘group’.

    You can take a look at this https://github.com/icebreaker/fz2d/blob/master/src/renderer/canvas_webgl.coffee#L125 to get an idea:

    * it will flush on state change (in this case texture only)
    * it will flush when the cache is full

  6. cliffski says:

    I did look into improving my batching yesterday, although its a bit of a pain, and opportunities are less than expected, because so many things are drawn in a different way. Some calls use different vertex formats,. different shaders, set shader variables to different values, even before you get to texture changes and render state changes.

    I did manage to halve the amount of draw calls for a l;ens ‘streak’ effect, which was about half of my DrawPrimitiveUP() calls. The others are engine glows that also use funny vertex formats and multi-texturing, but I’ll hopefully get them halved soon :D

  7. Long says:

    One thing to consider is maybe there is just too much stuff. This is a personal opinion, but I feel like the screen is too cluttered. Maybe the missiles’ trails are too long; maybe the lens-flare effect obscures the middle of the screen; I can’t put my finger on it, but it feels like I don’t see the big ships’ impact on the battle because the small ships and missiles have center stage in the picture.

  8. cliffski says:

    Indeed, it is not a good screenshot to demonstrate the game with TBH. Things are so very different when you see it in motion, because then you get the big obvious focuses of attention (The big ships, some asteroids) and then there is a general ‘busyness’ of small explosions, debris, missile and laser blasts etc which build up the atmosphere.

  9. ac says:

    Given this isn’t a “twitch” game, where as normally I am very serious about response times and such, in a game where the looks are among major selling points* , looking at the batching and pre-rendering possibilities seems the way to go.

    *for GSB2 vs 1 so far a lot of the blog has focused on the better gfx – If there’s other changes such as those related to modding (ideally ability to try new mods simply by connecting to a server that serves new mods directly like eg. TMNF), then I don’t think you’ve mentioned them quite enough as I can recall them now. (obv. such user made mods that are served directly pose a bit of a security hazard depending on how much modding is possible – ideally a game engine is a transparent development environment for things that don’t need to be able to interact with the system the game is running on, such that one can confidently download code/modules from other modders without fear of surprised – I’m quite reluctant of using eg. Microsoft’s nuget as it serves chains of opaque binaries that can get executed when you open a project containing them – Visual Studio does pop a warning about this but the only thing that does is scream “don’t do it!”).

  10. ac says:

    s/can recall/can’t recall/
    s/surprised/surprises/

  11. ac says:

    From engine dev. standpoint it would be reasonable to not make changes (unless already) that would preclude some form of play. eg. some form of extensive optimizations could make it more difficult to make a future version of GSB where the played could possibly eg. have the option to aim a laser cannon manually during a battle when time was right for it. That would essentially make GSB more close to MOO, where you had turn based combat that allowed to select what weapons to use against which target. I think it might be amusing to have the battle mostly-automated but leave user a time window during which they could finish off the opposing ships by selecting a weapon – completely optional but it might add more “feel of depth” that some reviewers of GSB1 desired.

  12. ac says:

    Screenshot:

    Many of the thin white fonts (Ambivale.. Speed, Admit Defeat) and the white thin aliased missile trails seem to be quite high in contrast vs the more blurred everything else. Whether this is a real issue is hard to say without seeing 1:1 lossless video capture or having the engine run here.

    This contrast does create the appearance (in that static image) that the missiles are closer than the ships shooting them.

    This well demonstrates why I think that with LCD you really need way more than ~100 DPI to get similar quality as with CRT in terms of not looking so aliased. I’ve been trying to gather evidence that even 320×200 modes pixels are not supposed to look like pixels on LCD with a good CRT emulation : maybe a combination of non-exact focus, phosphor glow smearing the pixels together, the shadow mask used not being like aperture grille or LCD pixel layout.

    I got a 14″ CRT TV and 15″ OLED TV and certainly the old SD TV programs still look better on the CRT but unfortunately I don’t have any shadow mask CRT’s for old games. I’d really like to have one for reference to check against the CRT emulation shaders – which hold potential for when desktop LCD/OLED goes high DPI.

    Overall I think the graphics seem a bit too blurry in the screenshot – I think it would be interesting to see what they looked like when using the new Nvidia/AMD driver downsampling (ie. have the game render at 2x resolution and the have display at 1x resolution). This should reduce the blurriness a great deal perhaps bringing the missile trails more in line with the rest of it instead of popping out so much.