Skip to main content
gamedev.net
🔒 Locked

Optimising 3D accelerated 2D rendering

Started by SyncViews Apr 7, 2011 at 12:06 AM 0 replies 1.9k views
Original Post
SyncViews
SyncViews
I want to optimise how I'm rendering my 2D game, since I'm not happy with how little (a few hundred on screen objects) it takes to cause the frame rate to drop noticebly on some of my hardware.

Currently I just draw stuff in a strict back to front order with the graphics layers not really knowing much about what is coming, I just go like "frigateImg->draw(x,y,angle,etc); lights->draw(); turrets->draw(); etc"...

This results in almost no opportunity to batch stuff since the draw order might be like:

frigate
blinking_blue_light
blinking_blue_light
heavy_turret
plasma_muzzle_effect
heavy_turret
med_turret
missile_turret
missile_launch_effect
missile_turret
missile_launch_effect
radar_dish
frigate
blinking_blue_light
blinking_blue_light
heavy_turret
plasma_muzzle_effect
heavy_turret
med_turret
missile_turret
missile_launch_effect
missile_turret
missile_launch_effect
radar_dish
...

As a result there is a lot of texture switches, and even if I batched draws of the same texture/etc, I would onyl be getting a few sprites in at most...

Ive been considering two approaches to improving this:

[subheading]Create a sprite sheet for as much as possible[/subheading]
The idea here is to put lots of sprites into a single image. However I'm not sure if it will be realistic to put some of the bigger sprites (upto 512*512 for stations, asteroids, and that, background is even bigger but I'm not counting it) given limitations on the maximun texture size (At least in D3D, I assume it s ahardware thing and OpenGL would have the same limits).

However I think this would still easyily give me like

frigate
--new batch--
blinking_blue_light
blinking_blue_light
heavy_turret
plasma_muzzle_effect
heavy_turret
med_turret
missile_turret
missile_launch_effect
missile_turret
missile_launch_effect
radar_dish
--new batch--
frigate
blinking_blue_light
blinking_blue_light
heavy_turret
plasma_muzzle_effect
heavy_turret
med_turret
missile_turret
missile_launch_effect
missile_turret
missile_launch_effect
radar_dish
...


[subheading]Reorder draws to be more optimimal[/subheading]
The idea here is to do the rendering in a more optimal order. Unfortunatly 99% of my stuff uses alpha blending so it must be back to front, however only in respect to things that actually overlapp, so I think it would be practical to end up with like the following since the event all ships are stacked ontop of each other is fairly low... However I assume this is how basically every 3D game must do it, since they likly have even more issues with the concept of packing everything into a single texture, vertex buffer, index buffer, etc, and I'm not sure its really possible to do anything at all in regards to shaders...

frigate
frigate
blinking_blue_light
blinking_blue_light
blinking_blue_light
blinking_blue_light
heavy_turret
heavy_turret
heavy_turret
heavy_turret
med_turret
med_turret
missile_turret
missile_turret
missile_turret
missile_turret
plasma_muzzle_effect
plasma_muzzle_effect
missile_launch_effect
missile_launch_effect
missile_launch_effect
missile_launch_effect
radar_dish
radar_dish
...


this one requires somwhat of a major change though and I havnt found much in the way of resources on how to do it
What I'm considering is making a bunch of "Drawable" objects which include everything needed to draw the object, position, angle, z order, etc, etc...

Somthing like:

class Drawable
{
int depth;
Vector2F pos;
RectF bounds;
bool backToFront;//must be rendered backToFront relative to all other backToFront objects, likly tobe 90% of my stuff, but I guess some things are "solid"

enum Type
{
SPRITE,
TEXT
};
Type type;
};
class Sprite : public Drawable
{
RectF uvs;
Texture *texture;
Vector2F centre;
Vector2F scale;
Vector2F
};
class Text : public Drawable
{
Font *font;
String text;

enum Align
{
LEFT = 0,
RIGHT = 1,
CENTRE_H = 2,

FIRST_LINE = 0,
TOP = 4,
BOTTOM = 8,
CENTRE_V = 16,

CENTRE = 2 | 16
};
int align;
};





class DrawGroup
{
enum Type
{
SPRITE,
TEXT;
};
Type type;
/**Draws the entire group in order from first to last*/
vitual void draw()=0;

std::vector<Drawable*> items;
};
class DrawSpriteGroup : public DrawGroup{...};
class DrawTextGroup : public DrawGroup{...};


std::vector<Drawable*> scene;//all the objects in the scene
std::vector<DrawGroup*> stuffToDrawThisFrameInOrder;//"how" the scene will be drawn


Of course the Drawable classes need some more fleshing out, like for example keeping the bounds thingupto date...

The problem is I'm not sure how to go from the "std::vector scene;" to the "std::vector stuffToDrawThisFrameInOrder;"..

I'm thinking that scene could then be sorted by depth, but then I'm not sure on the process of grouping things together



EDIT:
I'm also wondering now if just doing the first will be enough for my case. I'm thinking it will, and natually I'll do one first and test it before moving onto the other, however I'm still intrested in this ordering thing, and I also guess it has some relevance in 3D games (eg particle effects have lots of alpha stuff, and still need to be done in order relative to other things, like say windows).
Ashaman73
Ashaman73

I want to optimise how I'm rendering my 2D game, since I'm not happy with how little (a few hundred on screen objects) it takes to cause the frame rate to drop noticebly on some of my hardware.

I got a similar problem. I my case it is the gui which needed to be rendered in a certain order (alpha blending) and where I need to switch shaders and textures.


The idea here is to put lots of sprites into a single image. However I'm not sure if it will be realistic to put some of the bigger sprites (upto 512*512 for stations, asteroids, and that, background is even bigger but I'm not counting it) given limitations on the maximun texture size (At least in D3D, I assume it s ahardware thing and OpenGL would have the same limits).
[/quote]
The is a common approach (called texture atlas). Most modern hardware supports atleast 4kx4k textures.


The idea here is to do the rendering in a more optimal order. Unfortunatly 99% of my stuff uses alpha blending so it must be back to front, however only in respect to things that actually overlapp, so I think it would be practical to end up with like the following since the event all ships are stacked ontop of each other is fairly low... However I assume this is how basically every 3D game must do it, since they likly have even more issues with the concept of packing everything into a single texture, vertex buffer, index buffer, etc, and I'm not sure its really possible to do anything at all in regards to shaders...
[/quote]
The trouble is alpha blending. You need to render this in order. Everything else can be rendered using the z-buffer, so most things which are rendered in a 3d engine avoids alpha blending. You encounter alpha blending most likely for foilage/particles which needed to be in order and then will be rendered as large batch with very few texture/shader switches. Or you encounter alpha blending when handling transparency which gets ugly really quickly.

Eventually you have some hard problem and using alpha blending in combination with texture/shader switches is therefore avoided in most 3d engines. I would consider the following:
1. Try to remove real alpha blending and use only alpha masking (is rendered or not).
2. Hard edges could be solved by using some kind of AA.
3. Try to turn on zbuffer( depth= layer).
4. Divide your rendering pipeline into passes: alpha-masked, alpha-blending,particles,effects,overlays (works best with zbuffer).
5. Try to put as much sprites as possible into texture atlases for an according render pass (alpha-mask atlas)
6. Use batching for most rendering passes.
7. A 2d game has not lot of overdrawing so you could try to merge many shaders into a single uebershader to avoid shader switches.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.