|
Terrible Sprites Per Frame |
Paragon
Member #15,335
October 2013
|
I must be doing something terribly wrong. I am fairly certain that I am using DirectX, When I run it using OpenGL the frames number of sprites I can draw per frame drop even further. The profiler says that the majority of time is being spent in al_created_d3d_bitmap which, I can't figure out why that would be the case, b/c I initialize everything before the main loop, so by the time i start the profiler, It shouldn't even be called. RendererTest.cs 1#pragma once
2#include <allegro5\allegro.h>
3#include <allegro5\allegro_image.h>
4#include <allegro5/allegro_font.h>
5#include <allegro5/allegro_ttf.h>
6//#include <allegro5\allegro_opengl.h>
7#include <allegro5\allegro_direct3d.h>
8#include "WorldState.h"
9#include "Renderer.h"
10#include <boost/lexical_cast.hpp>
11#include <string.h>
12
13void DirectDraw(int drawCount,ALLEGRO_BITMAP* ply, BOX spriteBox);
14void RendererDraw(Renderer* renderer,int drawCount, ALLEGRO_BITMAP* ply, BOX spriteBox);
15
16void RunRendererTest()
17{
18 al_init();
19 al_install_keyboard();
20 al_init_image_addon();
21 al_init_ttf_addon();
22
23 al_set_new_display_option(ALLEGRO_RENDER_METHOD, 1, ALLEGRO_SUGGEST);
24 al_set_new_display_option(ALLEGRO_VSYNC, 2, ALLEGRO_SUGGEST);
25 al_set_new_display_option(ALLEGRO_CAN_DRAW_INTO_BITMAP, 1, ALLEGRO_SUGGEST);
26
27 //al_set_new_display_flags(ALLEGRO_OPENGL);
28 al_set_new_display_flags(ALLEGRO_DIRECT3D);
29
30 ALLEGRO_DISPLAY* display = al_create_display(800,600);
31 ALLEGRO_BITMAP* backBuffer = al_get_backbuffer(display);
32 ALLEGRO_FONT* font;
33
34 printf("RENDER METHOD: %i\n",al_get_display_option(display,ALLEGRO_RENDER_METHOD));
35 printf("VSYNC: %i\n",al_get_display_option(display,ALLEGRO_VSYNC));
36 printf("Can draw into bitmap: %i\n",al_get_display_option(display,ALLEGRO_CAN_DRAW_INTO_BITMAP));
37 printf("Single Buffer: %i\n",al_get_display_option(display,ALLEGRO_SINGLE_BUFFER));
38 printf("ALLEGRO_SWAP_METHOD: %i\n",al_get_display_option(display,ALLEGRO_SWAP_METHOD));
39 printf("ALLEGRO_AUX_BUFFERS: %i\n", al_get_display_option(display,ALLEGRO_AUX_BUFFERS));
40
41 int bitmapFlags = al_get_bitmap_flags(backBuffer);
42 if ((bitmapFlags&ALLEGRO_MEMORY_BITMAP)==ALLEGRO_MEMORY_BITMAP) printf("Memory Bitmap\n");
43 if ((bitmapFlags&ALLEGRO_VIDEO_BITMAP)==ALLEGRO_VIDEO_BITMAP) printf("Video Bitmap\n");
44
45 ALLEGRO_EVENT_QUEUE* systemQueue=al_create_event_queue();
46 al_register_event_source(systemQueue,al_get_keyboard_event_source());
47
48 WorldState ws;
49
50 ALLEGRO_PATH *path = al_get_standard_path(ALLEGRO_RESOURCES_PATH);
51 al_append_path_component(path, "Resources");
52 al_append_path_component(path, "Sprites");
53 al_set_path_filename(path, "blueShip.png");
54 ALLEGRO_BITMAP* ply=al_load_bitmap(al_path_cstr(path, '\\'));
55 if (!ply)
56 {
57 printf("ERROR Loading sprite");
58 ws.isDone=true;
59 }
60
61 al_set_path_filename(path,"spriteSheet.png");
62 ALLEGRO_BITMAP* spriteSheet=al_load_bitmap(al_path_cstr(path,'\\'));
63 if (!spriteSheet)
64 {
65 printf("ERROR Loading sprite sheet");
66 ws.isDone=true;
67 }
68 al_destroy_path(path);
69
70
71 path = al_get_standard_path(ALLEGRO_RESOURCES_PATH);
72 al_append_path_component(path, "Resources");
73 al_append_path_component(path, "Fonts");
74 al_set_path_filename(path, "pirulen.ttf");
75 font = al_load_ttf_font(al_path_cstr(path, '\\'),12,0);
76 if (!font)
77 {
78 printf("ERROR loading font");
79 ws.isDone = true;
80 }
81
82 ALLEGRO_EVENT evt;
83 evt.type=ALLEGRO_EVENT_KEY_UP;
84 while (evt.type!= ALLEGRO_EVENT_KEY_DOWN)
85 al_wait_for_event(systemQueue,&evt);
86
87 int drawCount=100;
88 int currCount=0;
89 Renderer r(display);
90
91 int w=al_get_bitmap_width(ply);
92 int h=al_get_bitmap_height(ply);
93 BOX spriteBox={0,0,w,h};
94 BOX tileBox={1,1,32,32};
95
96 while(!ws.isDone)
97 {
98 ws.Update();
99 //COORD p={0,6};
100 //SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),p);
101 ws.Draw();
102
103 if (!al_event_queue_is_empty(systemQueue))
104 {
105 ALLEGRO_EVENT ev;
106 al_wait_for_event(systemQueue,&ev);
107 if (ev.type==ALLEGRO_EVENT_KEY_DOWN)
108 {
109 if (ev.keyboard.keycode==ALLEGRO_KEY_ESCAPE)
110 ws.isDone=true;
111 if (ev.keyboard.keycode==ALLEGRO_KEY_A)
112 drawCount+=100;
113 if (ev.keyboard.keycode==ALLEGRO_KEY_Z)
114 drawCount-=100;
115 if (ev.keyboard.keycode==ALLEGRO_KEY_S)
116 drawCount+=10;
117 if (ev.keyboard.keycode==ALLEGRO_KEY_X)
118 drawCount-=10;
119 }
120 }
121 std::string o = "DrawCount:";
122 o = o + boost::lexical_cast<std::string>(drawCount);
123
124 std::string oo = "FPS:";
125 oo = oo + boost::lexical_cast<std::string>(ws.FPS);
126 al_clear_to_color(al_map_rgb(0, 0, 0));
127 al_draw_text(font, al_map_rgb(255, 255, 255), 200, 200, ALLEGRO_ALIGN_LEFT, o.c_str());
128 al_draw_text(font, al_map_rgb(255, 255, 255), 200, 300, ALLEGRO_ALIGN_LEFT, oo.c_str());
129 RendererDraw(&r, drawCount, spriteSheet, tileBox);
130 //al_clear_to_color(al_map_rgb(0,0,0));
131 //for(int i=0;i<drawCount;i++)
132 //{
133 // al_draw_tinted_scaled_rotated_bitmap_region(spriteSheet,
134 // tileBox.X1,tileBox.Y1,tileBox.W,tileBox.H,
135 // al_map_rgb(255,255,255),
136 // tileBox.X1+tileBox.W/2.0,tileBox.Y1+tileBox.H/2.0,
137 // i%300+100,(i/300)+100,
138 // 1,1,
139 // 0,0);
140 //}
141 //al_flip_display();
142 }
143 al_destroy_event_queue(systemQueue);
144}
145
146void RendererDraw(Renderer* renderer,int drawCount, ALLEGRO_BITMAP* ply, BOX spriteBox)
147{
148 for(int i=0;i<drawCount;i++)
149 {
150 BOX drawBox={i%300,(i/300),spriteBox.W,spriteBox.H};
151 renderer->Add(ply,spriteBox,drawBox,0,al_map_rgb(255,255,255),0);
152 }
153 renderer->Render();
154}
Renderer.h 1#include <vector>
2#include <cstdlib>
3
4struct BOX
5{
6 float X1;
7 float Y1;
8 float X2(){return X1+W;}
9 float Y2(){return Y1+H;}
10 float W;
11 float H;
12};
13
14struct renderInfo
15{
16 ALLEGRO_BITMAP* SpriteSheet;
17 BOX SpriteBox;
18 BOX DrawBox;
19 ALLEGRO_COLOR Tint;
20 float Rotation;
21 int Layer;
22 bool operator< (renderInfo &ri2) const
23 {
24 if (Layer!=ri2.Layer) return Layer<ri2.Layer;
25 if(DrawBox.Y1!=ri2.DrawBox.Y1) return DrawBox.Y1<ri2.DrawBox.Y1;
26 return false;
27 }
28};
29
30class Renderer
31{
32private:
33 std::vector<renderInfo> renderList;
34 renderInfo ri;
35 ALLEGRO_COLOR bgClearClr;
36public:
37 ALLEGRO_DISPLAY* display;
38
39 Renderer::Renderer(ALLEGRO_DISPLAY* d){
40 display = d;
41 bgClearClr=al_map_rgb(0,0,0);
42 renderList.reserve(5000);
43 al_set_target_backbuffer(d);
44 }
45
46 Renderer::~Renderer(void){}
47
48 void Renderer::Add(ALLEGRO_BITMAP* spriteSheet, BOX spriteBox, BOX drawBox, float rot, ALLEGRO_COLOR tint, int Layer)
49 {
50 renderInfo r={spriteSheet,spriteBox,drawBox,tint,rot,Layer};
51 renderList.push_back(r);
52 }
53
54 void Render()
55 {
56 std::sort(renderList.begin(),renderList.end());
57
58 for(unsigned int i=0;i<renderList.size();i++)
59 {
60 al_draw_tinted_scaled_rotated_bitmap_region(renderList[i].SpriteSheet,
61 renderList[i].SpriteBox.X1,renderList[i].SpriteBox.Y1,renderList[i].SpriteBox.W,renderList[i].SpriteBox.H,
62 renderList[i].Tint,
63 renderList[i].SpriteBox.X1+renderList[i].SpriteBox.W/2.0,renderList[i].SpriteBox.Y1+renderList[i].SpriteBox.H/2.0,
64 renderList[i].DrawBox.X1,renderList[i].DrawBox.Y1,
65 renderList[i].DrawBox.W/renderList[i].SpriteBox.W, renderList[i].DrawBox.H/renderList[i].SpriteBox.H,
66 renderList[i].Rotation,0);
67 }
68 renderList.clear();
69 al_flip_display();
70 }
71};
|
l j
Member #10,584
January 2009
|
std::sort(renderList.begin(),renderList.end()); This might be part of your problem, sorting the renderList every frame, sorting is quite an expensive operation (Sort only on frames when something new was added?). Also you might want to use al_draw_textf for your formatting instead of creating and appending to strings every frame. I think it performs better, but I'm not sure.
|
Trent Gamblin
Member #261
April 2000
|
al_hold_bitmap_drawing and atlases, if you're not.
|
Aikei_c
Member #14,871
January 2013
|
That's actually the biggest problem with allegro for me, I have terrible fps too, even when just drawing the same image over and over in a simple test. |
SiegeLord
Member #7,827
October 2006
|
It'd be very helpful if a self-contained example was posted. I certainly would like to know how al_created_d3d_bitmap shows up in a profiler (and what is the identity of that function, because no such function actually exists). "For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18 |
Kris Asick
Member #1,424
July 2001
|
Paragon said: Similiar code in C# (using XNA) requires 40k-50k sprites drawn per frame to drop it to 60FPS, Actually, that sounds about right if you're using mid-range hardware. Allegro has a lot of overhead going on in the background when you perform rendering calls, which is handled by the CPU, so if you need to exceed Allegro's limitations you need to get a little more creative. For instance, prepping everything for render using a single large texture in an array of vertices and texture coordinates, then rendering that entire thing in a single call to al_draw_prim(). One other thing you could do is look into calling al_hold_bitmap_drawing() at the start and end of your rendering blocks and making sure that all sprites using the same source bitmap (not necessarily the same sub-bitmap) are grouped together in their drawing calls, as the less times you have to switch the active texture on the GPU, the better. But I have to wonder, what the heck you could possibly be doing that requires using over 9000 sprites per frame... --- Kris Asick (Gemini) |
Paragon
Member #15,335
October 2013
|
@Kris Asick: @Trent Gamblin: @SiegeLord: Self contained source: 1#include <time.h>
2#include <stdio.h>
3#include <allegro5\allegro.h>
4#include <allegro5\allegro_image.h>
5#include <allegro5\allegro_font.h>
6#include <allegro5\allegro_ttf.h>
7//#include <allegro5\allegro_opengl.h>
8#include <allegro5\allegro_direct3d.h>
9
10ALLEGRO_BITMAP* LoadSprite()
11{
12 ALLEGRO_PATH *path = al_get_standard_path(ALLEGRO_RESOURCES_PATH);
13 al_append_path_component(path, "Resources");
14 al_append_path_component(path, "Sprites");
15 al_set_path_filename(path, "blueShip.png");
16 ALLEGRO_BITMAP* sprite = al_load_bitmap(al_path_cstr(path, '\\'));
17 if (!sprite)
18 printf("ERROR Loading sprite from %s", al_path_cstr(path,'\\'));
19 al_destroy_path(path);
20 return sprite;
21}
22
23ALLEGRO_FONT* LoadFont()
24{
25 ALLEGRO_PATH* path = al_get_standard_path(ALLEGRO_RESOURCES_PATH);
26 al_append_path_component(path, "Resources");
27 al_append_path_component(path, "Fonts");
28 al_set_path_filename(path, "pirulen.ttf");
29 ALLEGRO_FONT* font = al_load_ttf_font(al_path_cstr(path, '\\'), 12, 0);
30 if (!font)
31 printf("ERROR loading font from %s", al_path_cstr(path, '\\'));
32 al_destroy_path(path);
33 return font;
34}
35
36class WorldState
37{
38private:
39 clock_t _startTime;
40 clock_t _frameTime;
41 clock_t _totalElapsedClocks;
42 clock_t _clocksSinceLastUpdate;
43
44 clock_t elapsed;
45 int updates;
46 int draws;
47public:
48 bool isDone;
49 int UPS;
50 int FPS;
51 float TimeSinceLastUpdate;
52
53 float TotalElapsedTime;
54
55
56 WorldState()
57 {
58 _startTime = clock();
59 _frameTime = clock();
60 _totalElapsedClocks = 0;
61 _clocksSinceLastUpdate = 0;
62 elapsed = 0;
63 updates = 0;
64 draws = 0;
65 FPS = 0;
66 UPS = 0;
67 isDone = false;
68 }
69 ~WorldState(void){}
70 boolean Update(void)
71 {
72 clock_t newFrameTime = clock();
73 _clocksSinceLastUpdate = newFrameTime - _frameTime;
74 if (_clocksSinceLastUpdate <= 0)
75 return false;
76 updates++;
77 _totalElapsedClocks += _clocksSinceLastUpdate;
78 elapsed += _clocksSinceLastUpdate;
79
80 if (elapsed>CLOCKS_PER_SEC)
81 {
82 UPS = updates;
83 FPS = draws;
84 elapsed = 0;
85 updates = 0;
86 draws = 0;
87 }
88 _frameTime = newFrameTime;
89 return true;
90 }
91
92 void Draw(void)
93 {
94 draws++;
95 }
96};
97struct BOX
98{
99 float X1;
100 float Y1;
101 float X2(){ return X1 + W; }
102 float Y2(){ return Y1 + H; }
103 float W;
104 float H;
105};
106
107void main()
108{
109 al_init();
110 al_install_keyboard();
111 al_init_image_addon();
112 al_init_ttf_addon();
113
114 al_set_new_display_option(ALLEGRO_RENDER_METHOD, 1, ALLEGRO_REQUIRE);
115 al_set_new_display_option(ALLEGRO_VSYNC, 2, ALLEGRO_REQUIRE);
116 al_set_new_display_option(ALLEGRO_CAN_DRAW_INTO_BITMAP, 1, ALLEGRO_REQUIRE);
117
118 //al_set_new_display_flags(ALLEGRO_OPENGL);
119 al_set_new_display_flags(ALLEGRO_DIRECT3D);
120
121 ALLEGRO_DISPLAY* display = al_create_display(800, 600);
122 ALLEGRO_BITMAP* backBuffer = al_get_backbuffer(display);
123
124
125 printf("RENDER METHOD: %i\n", al_get_display_option(display, ALLEGRO_RENDER_METHOD));
126 printf("VSYNC: %i\n", al_get_display_option(display, ALLEGRO_VSYNC));
127 printf("Can draw into bitmap: %i\n", al_get_display_option(display, ALLEGRO_CAN_DRAW_INTO_BITMAP));
128 printf("Single Buffer: %i\n", al_get_display_option(display, ALLEGRO_SINGLE_BUFFER));
129 printf("ALLEGRO_SWAP_METHOD: %i\n", al_get_display_option(display, ALLEGRO_SWAP_METHOD));
130 printf("ALLEGRO_AUX_BUFFERS: %i\n", al_get_display_option(display, ALLEGRO_AUX_BUFFERS));
131
132 int bitmapFlags = al_get_bitmap_flags(backBuffer);
133 if ((bitmapFlags&ALLEGRO_MEMORY_BITMAP) == ALLEGRO_MEMORY_BITMAP) printf("Memory Bitmap\n");
134 if ((bitmapFlags&ALLEGRO_VIDEO_BITMAP) == ALLEGRO_VIDEO_BITMAP) printf("Video Bitmap\n");
135
136 ALLEGRO_EVENT_QUEUE* systemQueue = al_create_event_queue();
137 al_register_event_source(systemQueue, al_get_keyboard_event_source());
138
139 WorldState ws;
140
141 ALLEGRO_BITMAP* ply = LoadSprite();
142 ALLEGRO_FONT* font = LoadFont();
143
144 if (!font)
145 ws.isDone = true;
146 BOX spriteBox;
147 BOX tileBox;
148
149 if (ply){
150 if (al_is_compatible_bitmap(ply))
151 printf("Compatible");
152 else
153 printf("Incompatible");
154 int w = al_get_bitmap_width(ply);
155 int h = al_get_bitmap_height(ply);
156 spriteBox = { 0, 0, w, h };
157 tileBox = { 1, 1, w, h };
158 }
159 else
160 {
161 ws.isDone = true;
162 }
163
164
165 ALLEGRO_EVENT evt;
166 evt.type = ALLEGRO_EVENT_KEY_UP;
167 while (evt.type != ALLEGRO_EVENT_KEY_DOWN)
168 al_wait_for_event(systemQueue, &evt);
169
170 int drawCount = 100;
171 int currCount = 0;
172 while (!ws.isDone)
173 {
174 ws.Update();
175 ws.Draw();
176 if (!al_event_queue_is_empty(systemQueue))
177 {
178 ALLEGRO_EVENT ev;
179 al_wait_for_event(systemQueue, &ev);
180 if (ev.type == ALLEGRO_EVENT_KEY_DOWN)
181 {
182 if (ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
183 ws.isDone = true;
184 if (ev.keyboard.keycode == ALLEGRO_KEY_A)
185 drawCount += 1000;
186 if (ev.keyboard.keycode == ALLEGRO_KEY_Z)
187 drawCount -= 1000;
188 if (ev.keyboard.keycode == ALLEGRO_KEY_S)
189 drawCount += 100;
190 if (ev.keyboard.keycode == ALLEGRO_KEY_X)
191 drawCount -= 100;
192 }
193 }
194 al_clear_to_color(al_map_rgb(0, 0, 0));
195 /*DRAWING WITH LEAST AMOUNT OF OVERHEAD START*/
196 for (int i = 0; i<drawCount; i++)
197 {
198 al_draw_tinted_scaled_rotated_bitmap_region(ply,
199 0, 0, 32, 32,
200 al_map_rgb(255, 255, 255),
201 16, 16,
202 (i % 100)*5, (i / 100)*5,
203 1, 1,
204 0, 0);
205 }
206 al_draw_textf(font, al_map_rgb(255, 255, 255), 550, 10, ALLEGRO_ALIGN_LEFT, "Draw Count: %i", drawCount);
207 al_draw_textf(font, al_map_rgb(255, 255, 255), 550, 30, ALLEGRO_ALIGN_LEFT, "FPS: %i", ws.FPS);
208 al_flip_display();
209 /*DRAWING WITH LEAST AMOUNT OF OVERHEAD END*/
210 }
211 al_destroy_event_queue(systemQueue);
212}
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
There are a few things to mention. 1. You forgot al_hold_bitmap_drawing in your code. Even if you're only drawing one sprite it helps if you're drawing it many times at once. Wrap the loop with a pair of hold drawing calls the first true and the second false, and your drawing times should decrease substantially. 2. clock() returns total processor time on Linux/Unix, and total time past on Windows. It's not a good way to measure time. Use an ALLEGRO_TIMER to tick once a second if you want to count fps and ups. 3. You forgot al_init_font_addon. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
l j
Member #10,584
January 2009
|
void main() Only int main() or int main(int argc, char *argv[]) is legal C/C++.
|
Kris Asick
Member #1,424
July 2001
|
Paragon said: Do you have a link or can you elaborate on the part about using al_draw_prim()? Unfortunately, no. I'm speaking from experience in that, al_draw_prim() has a huge overhead cost to call, but can send massive amounts of data to the video card in that single call, so if you use it correctly it can be very powerful and can perform better than multiple al_draw_bitmap() calls, but to use it, you need to manually assign all of your texture coordinates, triangle coordinates, vertex colours, etc., and put them all into a massive array you can send to the function. Beyond that, just read the manual. Quote: Also, I don't particularly require 9000 sprites and certainly not 50k per frame, but I wanted see what the max was without any game logic so I could bench mark how badly various bits of game logic are slowing the game down... I just figured that if i started with the ability to draw 50k, it would give me more wiggle room for AI, and path finding then if i started @9k... does that make sense or am I thinking about it wrong? You're thinking about it wrong. You'd be surprised how much game logic you can process without affecting the framerate on today's computers. Heck, I was running particle engines at 70 FPS using Allegro 4 back in 2000 on original Pentium CPUs. Plus, if your game logic DOES slow the game down, there's almost certainly going to be alternative approaches. For instance, if you have a massive tile-based world and various objects in that world can update themselves at random, instead of scanning every single tile every game tick and performing those random calculations, you could just scan a handful of random tiles and make it more likely the random tile event will happen when it gets scanned. Or, if you have tile entities that need to constantly be updated, instead of scanning the entire map for changes, you make a list of each tile that needs to be updated every game tick and update that list as more such tiles are placed or removed. --- Kris Asick (Gemini) |
SiegeLord
Member #7,827
October 2006
|
So I profiled this on Windows, and didn't get anything sensible (I used the VerySleepy profiler). I checked, and there are no calls to _al_create_d3d_bitmap inside the draw loop, so if your profiler shows that as the main function, your profiler is broken. Anyway, I tested a few other things: I tried al_hold_bitmap_drawing as others have already suggested, and that made things 5x faster. Then I tried a light-weight replacement based on al_draw_prim in my FastDraw library and got it nearly 2x faster still (nearly 10x faster relative to the original). I am not aware of other techniques that will support faster drawing that the approach taken in that library given changing sprites every frame. If you can guarantee that you won't change your sprites often (e.g. it's a level tilemap) then Allegro 5.1 provides a feature to take it even further beyond, potentially adding 4x to 5x speed improvement on top of FastDraw's performance (i.e. perhaps as much as 50x faster than you current code). "For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18 |
Trent Gamblin
Member #261
April 2000
|
SiegeLord, I quickly looked at fast_draw.c, is it essentially the same thing as using al_draw_prim manually? I often do batch sprites up and draw them with al_draw_prim, would there be any difference besides ease of use?
|
SiegeLord
Member #7,827
October 2006
|
It's just for ease of use. "For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18 |
Trent Gamblin
Member #261
April 2000
|
Ok. I can see that being useful. I'm used to doing it manually. I'll try to remember it exists.
|
|