hi again.
ok, this is surely once again THE CLASSICAL QUESTIOn about CPU weight and allegro and blablablabla.
I begin without the app:
{"name":"state0.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/0\/5076464c4a2ed765dbb2a2fdd59a9e3a.jpg","w":511,"h":500,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/0\/5076464c4a2ed765dbb2a2fdd59a9e3a"}
The old version, without any rest(1) nor timer function:
{"name":"schwz3_state.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/3\/83af845627c7ee305ada2bea96a85b09.jpg","w":992,"h":468,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/3\/83af845627c7ee305ada2bea96a85b09"}
I have put in my main loop a rest of 1 sec, and putted in volatile funct activated by the timer all my printing to screen.
Notice here that I have only a poor bmp of 32k and less for mouse cursor...
Modification to the program with a rest and timer function, FPS 100:
{"name":"fps100.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/4\/5\/45dac6f09ba88ffcdcb54dd614e4603a.jpg","w":962,"h":435,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/4\/5\/45dac6f09ba88ffcdcb54dd614e4603a"}
FPS 50:
{"name":"fps50.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/d\/ad05f6a1c21056bbebca180a3c788b4b.jpg","w":955,"h":447,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/d\/ad05f6a1c21056bbebca180a3c788b4b"}
Here is part of the code, I do not arrive to do less to not charge the CPU. Any idea ?
1 | //////////////////////////////////////////////////////////////////////////////// |
2 | |
3 | volatile long screen_unit=0; |
4 | void screen_printing(void) |
5 | { |
6 | screen_unit++; |
7 | |
8 | //... mes functions d affichage |
9 | rectfill(buffer,0,0,440,400,makecol(255,255,255)); |
10 | textprintf_ex(buffer,font, 105,20,makecol(255,0,0),-1,AppliName);//affichage nom appi |
11 | draw_sprite(buffer,fond1,(int)-5,0); |
12 | |
13 | if (index_view_graph==1) |
14 | { rectfill(buffer, 320,350,330,360,makecol(255,0,0)); |
15 | view_midi_visual(); |
16 | } |
17 | |
18 | if (index_view_midiapps==1) |
19 | { rectfill(buffer, 150,350,160,360,makecol(255,0,0)); |
20 | listOfAppl(); |
21 | } |
22 | |
23 | |
24 | if ( index_write_deb==1){ |
25 | rectfill(buffer, 415, 370,425, 380,makecol(255,0,0)); |
26 | } |
27 | |
28 | |
29 | show_connected_to_midishare(); |
30 | textprintf_ex(buffer,font, 10,200,makecol(255,0,0),-1,"Midi INPUT: "); |
31 | textprintf_ex(buffer,font, 10,210,makecol(255,0,0),-1,"%s", my_midi_string); |
32 | |
33 | show_udp(); |
34 | choices(); |
35 | textprintf_ex(buffer,font, 10,180,makecol(255,0,0),-1,"FPS: %d ", FPS_defini); |
36 | draw_sprite(buffer,mouse,(int)mouse_x,mouse_y); |
37 | blit(buffer,screen,0,0,0,0,442,442); |
38 | |
39 | } |
40 | END_OF_FUNCTION(screen_printing) |
41 | |
42 | |
43 | |
44 | //////////////////////////////////////////////////////////////////////////////// |
45 | int main() |
46 | { |
47 | allegro_init(); |
48 | install_keyboard(); |
49 | install_mouse(); |
50 | install_timer(); |
51 | LOCK_FUNCTION(screen_printing); |
52 | LOCK_VARIABLE(screen_unit); |
53 | |
54 | set_color_depth(16); |
55 | set_gfx_mode(GFX_AUTODETECT_WINDOWED,442,442,0,0); |
56 | if(set_gfx_mode(GFX_AUTODETECT_WINDOWED,440,400,0,0)!=0) |
57 | { |
58 | set_gfx_mode(GFX_TEXT,0,0,0,0); |
59 | allegro_message("%s\nSchwartz_Peter's Video Mode:\n 1024x768 and Higher\n16 or 32 bits better look\n\n", allegro_error); |
60 | quit_funct();return 1; |
61 | } |
62 | fond1=load_bitmap("chaton2.tga",NULL); |
63 | mouse=load_bitmap("curseur1.bmp",NULL); |
64 | |
65 | buffer=create_bitmap(SCREEN_W,SCREEN_H); |
66 | blit(buffer,screen,0,0,0,0,442,442); |
67 | |
68 | if(set_display_switch_mode(SWITCH_BACKGROUND)) |
69 | {set_display_switch_mode(SWITCH_BACKAMNESIA);} |
70 | |
71 | |
72 | load_ip_conf(); |
73 | SetUpMidi(); |
74 | |
75 | initialisation_client(); |
76 | |
77 | |
78 | while (index_quit==0) |
79 | { |
80 | rest(1); |
81 | |
82 | |
83 | |
84 | install_int_ex(screen_printing,BPS_TO_TIMER(FPS_defini)); |
85 | |
86 | |
87 | |
88 | } |
89 | |
90 | |
91 | quit_funct(); |
92 | |
93 | |
94 | |
95 | return 0; |
96 | } |
97 | END_OF_MAIN(); |
umpf !!!
Chris, n'appelle install_int_ex qu'une seule fois ! et en dehors de la boucle while !
Apres quoi tu pourras boucler sur ton rest(1); tranquillement !
First of all you shouldn't be doing so much work in a timer function. Setting a flag or incrementing a counter and responding to it in the game loop is the preferred (and feasible) approach. I'm not sure why you repeatedly call install_int_ex, but that seems shifty as well...
I'm surprised that doesn't outright crash, what with Allegro's notorious thread unsafety and all.
You can attempt to rest() for more than 1ms at once.
Try 5, 10, 15, 20.
edit
As for the drawing part, chances are the biggest job for the CPU is the
Considering that less than 10% of the screen surface may have changed, you could just blit these parts individually, like a "dirty rectangles" system.
(Additionally, you can memorize if these screen parts HAVE changed at all. For example, the top text: No need to redraw the paragraph if it has the same content as when it was last redrawn.)
Is it Tuesday again?
Are there any advantages of using semaphores over normal high-res timers?
ooooups, i didn t know it was an error, I have an hudge programm wich is running with install_ex inside of the main loop since 4 years of dev... without any error i mean errors of timers (and its real time manipulation)...
but its still doing nothing o my troubles of CPU...
with:
while (index_quit==0) { rest(1); //nothing... but refreshment is done in the volatile as previous and first code }
CPU weight is still from 41 % and up to 70%
{"name":"outofloop.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/3\/032ede2c76a20c40b9e54221cb2392ea.jpg","w":983,"h":510,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/3\/032ede2c76a20c40b9e54221cb2392ea"}
so? I mean install_ex is there tomake use of timer in an easy manner, to avoid calculations of tclock ?
install_int* granularity can vary from 1 msec to 14-20 msec
Are there any advantages of using semaphores over normal high-res timers?
Yay. Someone asked the RIGHT question.
Why did i use a locking semaphore? Because allegro timers spawn their own thread, wake it up, run user code (increment value), and then go back to sleep.
I used a semaphore because i wanted to put the mail loop to sleep and have the timer wake it up like it should happen. You only want your main loop to execute when the timer beeps! Allegro timer simplifies this possibility away but certain operating systems (not all) libraries can handle this without a problem.
Rest is basically a way to stop constant spin locking by taking shifts. It will tell the operating system to put the thread to sleep... but only for a really short time. It then checks again.. whenever the operating system feels like it. It may check almost right away (bad because more cpu wasted) but the general rule of thumb is that rest is only accurate to within 15ms.
To summarize: rest still wastes tons of cpu. It constantly wakes up and goes back to sleep.
With my locking mechanism i can get my own (graphically simple) allegrogl games to use < 1% cpu.
That's really cool Goalie Ca, but how portable is it...? Will your example work on Windows and Linux? Do you need to install third party libraries or anything?
If your OS is posix then you have it. All linux, mac, and older winnt. For new windows you need to download pthread. http://sourceware.org/pthreads-win32/
If you really wanted to you could use windows locking mechanism. It is in winapi and does the same thing.
I guess I will attempt to learn to use semaphores then. Until then, here is my revised raw timer template...
1 | /* |
2 | * Name: fps.h |
3 | * Author: bamccaig->members.allegro.cc |
4 | * Purpose: Example of using Allegro timer routines to limit frames-per-second |
5 | * in a game. |
6 | * Disclaimer: Use at own risk. :P |
7 | */ |
8 | #include <allegro.h> |
9 | #include <stdio.h> |
10 | #include <stdlib.h> |
11 | |
12 | #define bool short int |
13 | #define false 0 |
14 | #define true -1 |
15 | |
16 | #define SECS_PER_MIN 60 |
17 | #define MINS_PER_HOUR 60 |
18 | #define HOURS_PER_DAY 24 |
19 | #define TARGET_FPS 30 |
20 | #define WHITE makecol(255, 255, 255) |
21 | |
22 | volatile bool gblnCloseButtonPressed = false; // Flag for window close (X) button status. |
23 | volatile unsigned short int gushrFramesPerSecond = 0; // Count of frames last second (Set). |
24 | volatile unsigned short int gushrFramesThisSecond = 0; // Count of frames this second (Increment). |
25 | volatile unsigned int guintPendingFrames = 0; // Number of frames OK'd to be processed. |
26 | volatile unsigned int guintTotalFrames = 0; // Total number of frames. |
27 | |
28 | volatile unsigned short int gushrSeconds = 0; |
29 | unsigned short int gushrMinutes = 0; |
30 | unsigned short int gushrHours = 0; |
31 | unsigned short int gushrDays = 0; |
32 | |
33 | void add_frame(void); // Timer routine. |
34 | void close_button_handler(void); // Window X button handler on supported platforms. |
35 | void draw(BITMAP*); // Draws the scene. |
36 | int initialize(void); // Initializes the application. |
37 | void logic(void); // Processes gameplay. |
38 | void tick(void); // Timer routine. |
39 | void update_time(void); // Updates clock variables. |
1 | /* |
2 | * Name: fps.c |
3 | * Author: bamccaig->members.allegro.cc |
4 | * Purpose: Example of using Allegro timer routines to limit frames-per-second |
5 | * in a game. |
6 | * Disclaimer: Use at own risk. :P |
7 | */ |
8 | #include "fps.h" |
9 | |
10 | int main(int argc, char* argv[]) |
11 | { |
12 | BITMAP* bmpBuffer = NULL; |
13 | |
14 | // Try to initialize game. |
15 | if(initialize() != 0) |
16 | exit(-1); |
17 | |
18 | // Create and clear screen buffer. |
19 | bmpBuffer = create_bitmap(800, 600); |
20 | clear(bmpBuffer); |
21 | |
22 | // Main game loop. |
23 | while(!(key[KEY_ESC] || gblnCloseButtonPressed)) |
24 | { |
25 | // Rest loop. Nothing to do until next frame so we "go to sleep" temporarily. |
26 | while(!guintPendingFrames) |
27 | rest(16); |
28 | |
29 | // Logic loop. Changes to the game happen here. |
30 | while(guintPendingFrames) |
31 | { |
32 | logic(); |
33 | guintPendingFrames--; |
34 | } |
35 | |
36 | /* |
37 | * Draw. Here we draw the current frame first to a buffer in main memory and |
38 | * then to the video memory (screen). |
39 | */ |
40 | draw(bmpBuffer); |
41 | } |
42 | |
43 | // Release buffer. |
44 | destroy_bitmap(bmpBuffer); |
45 | |
46 | return(0); |
47 | } |
48 | END_OF_MAIN() |
49 | |
50 | |
51 | |
52 | void add_frame(void) |
53 | /* |
54 | * This is a timer routine that signals the main game loop to execute the logic (and redraw). |
55 | * Every time it executes it increments a counter. When the counter is non-zero the main game |
56 | * loop will execute the logic and then decrement the counter. Logic only happens once |
57 | * for every execution of this timer routine. Drawing happens at most once, but perhaps less |
58 | * if the program starts to get behind and needs to catch up again by repeating logic. |
59 | */ |
60 | { |
61 | guintPendingFrames++; |
62 | } |
63 | END_OF_FUNCTION(add_frame) |
64 | |
65 | |
66 | |
67 | void close_button_handler(void) |
68 | /* |
69 | * Handles the X button of the window on supported platforms. Signals the main game loop |
70 | * to exit. |
71 | */ |
72 | { |
73 | gblnCloseButtonPressed = true; |
74 | } |
75 | END_OF_FUNCTION(close_button_callback) |
76 | |
77 | |
78 | |
79 | void draw(BITMAP* bmpBuffer) |
80 | /* |
81 | * Draws the current frame to a BITMAP buffer in main memory and then to the video memory (screen). |
82 | * In this example we're only drawing the total frame count and the frames-per-second count. |
83 | */ |
84 | { |
85 | // Draw to buffer. |
86 | textprintf_ex(bmpBuffer, font, 20, 20, WHITE, -1, "frame-count: %d", guintTotalFrames); |
87 | textprintf_ex(bmpBuffer, font, 20, 40, WHITE, -1, "time: %03d:%02d:%02d:%02d", gushrDays, gushrHours, gushrMinutes, gushrSeconds); |
88 | textprintf_ex(bmpBuffer, font, 20, 60, WHITE, -1, "fps: %d", gushrFramesPerSecond); |
89 | |
90 | // Draw to screen. |
91 | blit(bmpBuffer, screen, 0, 0, 0, 0, 800, 600); |
92 | clear(bmpBuffer); |
93 | } |
94 | END_OF_FUNCTION(draw) |
95 | |
96 | |
97 | |
98 | int initialize(void) |
99 | // Initializes the program. |
100 | { |
101 | char* chrGLaDOS_msg = "If at first you don't succeed,...you fail"; |
102 | |
103 | // Try to initialize allegro. |
104 | if(allegro_init() != 0) |
105 | { |
106 | printf("%s [to initialize Allegro].\n", chrGLaDOS_msg); |
107 | return(-1); |
108 | } |
109 | |
110 | // Try to install keyboard. |
111 | if(install_keyboard() != 0) |
112 | { |
113 | printf("%s [to install the keyboard routines].\n", chrGLaDOS_msg); |
114 | return(-1); |
115 | } |
116 | |
117 | // Try to install timer. |
118 | if(install_timer() != 0) |
119 | { |
120 | printf("%s [to install the timer routines].", chrGLaDOS_msg); |
121 | return(-1); |
122 | } |
123 | |
124 | // Set color depth. |
125 | set_color_depth(32); |
126 | |
127 | // Try to set graphic mode. |
128 | if(set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0) != 0) |
129 | { |
130 | printf("%s [to set the graphics mode]. %s.\n", chrGLaDOS_msg, allegro_error); |
131 | return(-1); |
132 | } |
133 | |
134 | /* |
135 | * Set display switch mode so the game will continue processing even if the |
136 | * user switches to another window. |
137 | */ |
138 | if(set_display_switch_mode(SWITCH_BACKGROUND) != 0) |
139 | { |
140 | printf("%s [to set display switch mode].\n", chrGLaDOS_msg, allegro_error); |
141 | return(-1); |
142 | } |
143 | |
144 | // Lock timer variables. |
145 | LOCK_VARIABLE(gblnCloseButtonPressed); |
146 | LOCK_VARIABLE(guintPendingFrames); |
147 | LOCK_VARIABLE(gushrFramesPerSecond); |
148 | LOCK_VARIABLE(gushrFramesThisSecond); |
149 | LOCK_VARIABLE(gushrSeconds); |
150 | LOCK_VARIABLE(gushrMinutes); |
151 | LOCK_VARIABLE(gushrHours); |
152 | LOCK_VARIABLE(gushrDays); |
153 | |
154 | // Lock timer routines. |
155 | LOCK_FUNCTION(add_frame); |
156 | LOCK_FUNCTION(close_button_handler); |
157 | LOCK_FUNCTION(tick); |
158 | |
159 | // Install timer callbacks. |
160 | install_int_ex(add_frame, BPS_TO_TIMER(TARGET_FPS)); |
161 | install_int_ex(tick, BPS_TO_TIMER(1)); |
162 | |
163 | // Install close-button callback. |
164 | set_close_button_callback(close_button_handler); |
165 | |
166 | return(0); |
167 | } |
168 | END_OF_FUNCTION(initialize) |
169 | |
170 | |
171 | |
172 | void logic(void) |
173 | /* |
174 | * Logic. In this example we're only incrementing a total frame counter, a |
175 | * frames-per-second counter, and updating time variables. |
176 | */ |
177 | { |
178 | update_time(); |
179 | |
180 | guintTotalFrames++; |
181 | gushrFramesThisSecond++; |
182 | } |
183 | END_OF_FUNCTION(logic) |
184 | |
185 | |
186 | |
187 | void tick(void) |
188 | /* |
189 | * This is another timer routine that executes [approximately] once per second. |
190 | * It's role is to set the frames-per-second count and reset the |
191 | * frames-this-second counter. |
192 | */ |
193 | { |
194 | // Update game time. |
195 | gushrSeconds++; |
196 | |
197 | // Update frames per second. |
198 | gushrFramesPerSecond = gushrFramesThisSecond; |
199 | gushrFramesThisSecond = 0; |
200 | } |
201 | END_OF_FUNCTION(tick) |
202 | |
203 | |
204 | |
205 | void update_time(void) |
206 | // Updates time variables. |
207 | { |
208 | if(gushrSeconds == SECS_PER_MIN) |
209 | { |
210 | gushrMinutes++; |
211 | gushrSeconds = 0; |
212 | } |
213 | |
214 | if(gushrMinutes == MINS_PER_HOUR) |
215 | { |
216 | gushrHours++; |
217 | gushrMinutes = 0; |
218 | } |
219 | |
220 | if(gushrHours == HOURS_PER_DAY) |
221 | { |
222 | gushrDays++; |
223 | gushrHours = 0; |
224 | } |
225 | } |
To compile it the following commands should work (you must have Allegro installed).
Windows/MinGW: gcc -O2 fps.c -o fps.exe -lalleg
*nix/GCC: gcc -O2 fps.c -o fps $(allegro-config --libs)
I guess I will attempt to learn to use semaphores then.
Simple. A semaphore is like a locking counter. I've pasted the code i wrote to the wiki and have added a few useful comments. Basically main goes to sleep, ticker wakes it up. No wasted cycles, no extra context switches, no spinlocks.
1 | #include <allegro.h> |
2 | #include <semaphore.h> |
3 | sem_t ticks; //our new "counter" variable. |
4 | |
5 | |
6 | void ticker(){ |
7 | sem_post(&ticks); //increases count. wakes up first waiting thread (sem_wait) if count > 0 |
8 | }END_OF_FUNCTION(ticker) |
9 | |
10 | void main(){ |
11 | setup_stuff(); |
12 | |
13 | sem_init(&ticks, 0, 1); //sets a good default value to count. (count = 1) |
14 | LOCK_FUNCTION(ticker); |
15 | install_int_ex(ticker,BPS_TO_TIMER(60)); |
16 | |
17 | while(game_on){ |
18 | sem_wait(&ticks); //decreases count, goes to sleep if count < 0, wakes back up when count >= 0 (through sem_post) |
19 | update_stuff(); |
20 | draw_stuff(); |
21 | } |
22 | }END_OF_MAIN() |
OK finally I founded why it was taking so many weight in memory:
im using
if(set_display_switch_mode(SWITCH_BACKGROUND)) {set_display_switch_mode(SWITCH_BACKAMNESIA);}
to enable the visual refreshing of information if I m not on its window.
... oups !
while(game_on){ sem_wait(&ticks); //decreases count, goes to sleep if count < 0, wakes back up when count >= 0 (through sem_post) update_stuff(); draw_stuff(); }
If you want to do frameskipping, this should work:
while(game_on){ sem_wait(&ticks); do { update_stuff(); } while(sem_trywait(&ticks) == 0); draw_stuff(); }
MAOUW its nice, but it need to run , while its not on nor selected.
So in fact putting in an install_ex routines enable not to freeze the tasks, just the screen refreshment is not done ( wich take a HUDGE place in mem)
I'm surprised that doesn't outright crash, what with Allegro's notorious thread unsafety and all.
A lot of the crashieness was "fixed" in the last release or two. But it means theres a ton of locking and unlocking going on, slowing things down and potentially using lots of cpu. That doesn't mean you should use the same allegro resources from multiple thread (in fact you probably shouldn't), unless you're absolutely sure you know what you're doing.
1 | /* |
2 | * Name: fps2.h |
3 | * Author: bamccaig->members.allegro.cc |
4 | * Purpose: Example of using Allegro timer routines and semaphores to limit frames-per-second |
5 | * in a game and reduce wasted cycles. |
6 | * Disclaimer: Use at own risk. :P |
7 | */ |
8 | #include <allegro.h> |
9 | #include <semaphore.h> |
10 | #include <stdio.h> |
11 | #include <stdlib.h> |
12 | |
13 | #define bool short int |
14 | #define false 0 |
15 | #define true -1 |
16 | |
17 | #define SECS_PER_MIN 60 |
18 | #define MINS_PER_HOUR 60 |
19 | #define HOURS_PER_DAY 24 |
20 | #define TARGET_FPS 30 |
21 | #define WHITE makecol(255, 255, 255) |
22 | |
23 | sem_t gSemaphore; |
24 | |
25 | volatile bool gblnCloseButtonPressed = false; // Flag for window close (X) button status. |
26 | volatile unsigned short int gushrFramesPerSecond = 0; // Count of frames last second (Set). |
27 | volatile unsigned short int gushrFramesThisSecond = 0; // Count of frames this second (Increment). |
28 | volatile unsigned int guintTotalFrames = 0; // Total number of frames. |
29 | |
30 | volatile unsigned short int gushrSeconds = 0; |
31 | unsigned short int gushrMinutes = 0; |
32 | unsigned short int gushrHours = 0; |
33 | unsigned short int gushrDays = 0; |
34 | |
35 | void add_frame(void); // Timer routine. |
36 | void close_button_handler(void); // Window X button handler on supported platforms. |
37 | void draw(BITMAP*); // Draws the scene. |
38 | int initialize(void); // Initializes the application. |
39 | void logic(void); // Processes gameplay. |
40 | void tick(void); // Timer routine. |
41 | void update_time(void); // Updates clock variables. |
1 | /* |
2 | * Name: fps2.c |
3 | * Author: bamccaig->members.allegro.cc |
4 | * Purpose: Example of using Allegro timer routines and semaphores to limit frames-per-second |
5 | * in a game and reduce wasted cycles. |
6 | * Disclaimer: Use at own risk. :P |
7 | */ |
8 | #include "fps2.h" |
9 | |
10 | int main(int argc, char* argv[]) |
11 | { |
12 | BITMAP* bmpBuffer = NULL; |
13 | |
14 | // Try to initialize game. |
15 | if(initialize() != 0) |
16 | exit(-1); |
17 | |
18 | // Initalize semaphore. |
19 | sem_init(&gSemaphore, 0, 1); |
20 | |
21 | // Create and clear screen buffer. |
22 | bmpBuffer = create_bitmap(800, 600); |
23 | clear(bmpBuffer); |
24 | |
25 | // Main game loop. |
26 | while(!(key[KEY_ESC] || gblnCloseButtonPressed)) |
27 | { |
28 | // Sleep until next frame. |
29 | sem_wait(&gSemaphore); |
30 | |
31 | // Logic loop. Changes to the game happen here. |
32 | logic(); |
33 | |
34 | /* |
35 | * Draw. Here we draw the current frame first to a buffer in main memory and |
36 | * then to the video memory (screen). |
37 | */ |
38 | draw(bmpBuffer); |
39 | } |
40 | |
41 | // Release buffer. |
42 | destroy_bitmap(bmpBuffer); |
43 | |
44 | return(0); |
45 | } |
46 | END_OF_MAIN() |
47 | |
48 | |
49 | |
50 | void add_frame(void) |
51 | /* |
52 | * This is a timer routine that signals the main game loop to execute the logic (and redraw). |
53 | */ |
54 | { |
55 | // Wake up game. |
56 | sem_post(&gSemaphore); |
57 | } |
58 | END_OF_FUNCTION(add_frame) |
59 | |
60 | |
61 | |
62 | void close_button_handler(void) |
63 | /* |
64 | * Handles the X button of the window on supported platforms. Signals the main game loop |
65 | * to exit. |
66 | */ |
67 | { |
68 | gblnCloseButtonPressed = true; |
69 | } |
70 | END_OF_FUNCTION(close_button_callback) |
71 | |
72 | |
73 | |
74 | void draw(BITMAP* bmpBuffer) |
75 | /* |
76 | * Draws the current frame to a BITMAP buffer in main memory and then to the video memory (screen). |
77 | * In this example we're only drawing the total frame count and the frames-per-second count. |
78 | */ |
79 | { |
80 | // To buffer. |
81 | textprintf_ex(bmpBuffer, font, 20, 20, WHITE, -1, "frame-count: %d", guintTotalFrames); |
82 | textprintf_ex(bmpBuffer, font, 20, 40, WHITE, -1, "time: %03d:%02d:%02d:%02d", gushrDays, gushrHours, gushrMinutes, gushrSeconds); |
83 | textprintf_ex(bmpBuffer, font, 20, 60, WHITE, -1, "fps: %d", gushrFramesPerSecond); |
84 | |
85 | // To screen. |
86 | blit(bmpBuffer, screen, 0, 0, 0, 0, 800, 600); |
87 | clear(bmpBuffer); |
88 | } |
89 | END_OF_FUNCTION(draw) |
90 | |
91 | |
92 | |
93 | int initialize(void) |
94 | // Initializes the program. |
95 | { |
96 | char* chrGLaDOS_msg = "If at first you don't succeed,...you fail"; |
97 | |
98 | // Try to initialize allegro. |
99 | if(allegro_init() != 0) |
100 | { |
101 | printf("%s [to initialize Allegro]. %s.\n", chrGLaDOS_msg, allegro_error); |
102 | return(-1); |
103 | } |
104 | |
105 | // Try to install keyboard. |
106 | if(install_keyboard() != 0) |
107 | { |
108 | printf("%s [to install the keyboard routines].\n", chrGLaDOS_msg); |
109 | return(-1); |
110 | } |
111 | |
112 | // Try to install timer. |
113 | if(install_timer() != 0) |
114 | { |
115 | printf("%s [to install the timer routines].", chrGLaDOS_msg); |
116 | return(-1); |
117 | } |
118 | |
119 | // Set color depth. |
120 | set_color_depth(32); |
121 | |
122 | // Try to set graphic mode. |
123 | if(set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0) != 0) |
124 | { |
125 | printf("%s [to set the graphics mode]. %s.\n", chrGLaDOS_msg, allegro_error); |
126 | return(-1); |
127 | } |
128 | |
129 | /* |
130 | * Set display switch mode so the game will continue processing even if the |
131 | * user switches to another window. |
132 | */ |
133 | if(set_display_switch_mode(SWITCH_BACKGROUND) != 0) |
134 | { |
135 | printf("%s [to set display switch mode].\n", chrGLaDOS_msg, allegro_error); |
136 | return(-1); |
137 | } |
138 | |
139 | // Lock timer variables. |
140 | LOCK_VARIABLE(gblnCloseButtonPressed); |
141 | LOCK_VARIABLE(guintPendingFrames); |
142 | LOCK_VARIABLE(gushrFramesPerSecond); |
143 | LOCK_VARIABLE(gushrFramesThisSecond); |
144 | LOCK_VARIABLE(gushrSeconds); |
145 | LOCK_VARIABLE(gushrMinutes); |
146 | LOCK_VARIABLE(gushrHours); |
147 | LOCK_VARIABLE(gushrDays); |
148 | |
149 | // Lock timer routines. |
150 | LOCK_FUNCTION(add_frame); |
151 | LOCK_FUNCTION(close_button_handler); |
152 | LOCK_FUNCTION(tick); |
153 | |
154 | // Install timer callbacks. |
155 | install_int_ex(add_frame, BPS_TO_TIMER(TARGET_FPS)); |
156 | install_int_ex(tick, BPS_TO_TIMER(1)); |
157 | |
158 | // Install close-button callback. |
159 | set_close_button_callback(close_button_handler); |
160 | |
161 | return(0); |
162 | } |
163 | END_OF_FUNCTION(initialize) |
164 | |
165 | |
166 | |
167 | void logic(void) |
168 | /* |
169 | * Logic. In this example we're only incrementing a total frame counter, a |
170 | * frames-per-second counter, and updating time variables. |
171 | */ |
172 | { |
173 | update_time(); |
174 | |
175 | guintTotalFrames++; |
176 | gushrFramesThisSecond++; |
177 | } |
178 | END_OF_FUNCTION(logic) |
179 | |
180 | |
181 | |
182 | void tick(void) |
183 | /* |
184 | * This is another timer routine that executes [approximately] once per second. |
185 | * It's role is to set the frames-per-second count and reset the |
186 | * frames-this-second counter. |
187 | */ |
188 | { |
189 | // Update game time. |
190 | gushrSeconds++; |
191 | |
192 | // Update frames per second. |
193 | gushrFramesPerSecond = gushrFramesThisSecond; |
194 | gushrFramesThisSecond = 0; |
195 | } |
196 | END_OF_FUNCTION(tick) |
197 | |
198 | |
199 | |
200 | void update_time(void) |
201 | // Updates time variables. |
202 | { |
203 | if(gushrSeconds == SECS_PER_MIN) |
204 | { |
205 | gushrMinutes++; |
206 | gushrSeconds = 0; |
207 | } |
208 | |
209 | if(gushrMinutes == MINS_PER_HOUR) |
210 | { |
211 | gushrHours++; |
212 | gushrMinutes = 0; |
213 | } |
214 | |
215 | if(gushrHours == HOURS_PER_DAY) |
216 | { |
217 | gushrDays++; |
218 | gushrHours = 0; |
219 | } |
220 | } |
To compile it the following commands should work (you must have Allegro [and pthreads installed - Windows users, this probably means you]).
Windows/MinGW: gcc -O2 fps2.c -o fps2.exe -lalleg -lpthreadGC2
*nix/GCC: gcc -O2 fps2.c -o fps2.exe $(allegro-config --libs) -lpthread
I'm surprised that doesn't outright crash, what with Allegro's notorious thread unsafety and all.
never crashed... 4 years this damn calling inside main loop... hundred of performances and theatre play without crash...
unless you're absolutely sure you know what you're doing.
well, or people have made studies, and better than say'until you know what you are doing' they can explain WHY and HOW ( more constructiv), or they didn t ... no ?
well, or people have made studies, and better than say'until you know what you are doing' they can explain WHY and HOW ( more constructiv), or they didn t ... no ?
I don't understand a thing you just said.
However, what i meant was, if you know the issues inherent with threading, and know what to work around them, do as you please.
On a side note, should a semaphore (sem_t) be volatile?
No. Just ignore that keyword. It doesn't do what most people think it does.
On a side note, should a semaphore (sem_t) be volatile?
Actually it relies on a special CPU instruction called (roughly) "test and set lock". It is an entirely atomic operation. sem_t itself is a struct.
No. Just ignore that keyword. It doesn't do what most people think it does.
Is this not what it does?
He must be referring to the fact that people assume volatile is atomic. That is it reads, processes, and writes back.
I think of Volatile as the (sort of) opposite of "register". That is, this is not to be cached in a register. Write-through!!! Read too!
Thanks, Goalie Ca! My raw Allegro timer example uses approximately 20% CPU. The modified version implementing pthreads/semaphores is using 3-5% CPU.
** EDIT **
I may have spoken too soon. The 20% CPU usage was seen on another system. My home system is actually using a very similar amount of CPU with raw Allegro timers...
Also, see this: http://kerneltrap.org/Linux/Volatile_Superstition
I may have spoken too soon. The 20% CPU usage was seen on another system. My home system is actually using a very similar amount of CPU with raw Allegro timers...
rest() will vary from system to system. It is dependant on the operating system scheduler. It may even vary from session to session. Might vary if you run it in win2k instead of vista.
I also wonder about pthread performance on windows. Windows does not have native posix threading. Windows threads will have to be "Wrapped" in order to have the necessary behaviour.
I can take my code and run it on a junker linux system and it will use < 1%.
BTW, is there a way to do this without relying on an Allegro timer? It's just that if you're trying to move away from a while(waiting) rest(1); loop, that's basically what Allegro's timers do. Though the timer thread does attempt to use precise resting, and calculating the rest time 'til it needs to wake up next, it still won't get any more accurate than 75 to 100 fps (since once it goes to sleep, the thread won't wake up again for about 10ms, which maxes you out at 100 tics per second; and if you're trying for 100fps, then you're not getting any better than a rest(1) anyway.. or worse, rest past the intended wakeup time causing an additional 10ms delay; then add the overhead of the callback loop itself, plus other timers trying to run..).
Obviously there'd be no portable way to do it, but I think having the system interrupt and call into your process at specific intervals to do a simple sem_post wouldn't be too hard to wrap.
Hmm.. that's good food for thought. I would have thought the allegro timers would use the underlying operating system ones (eg: windows highperformance timer). Granted.. none are "real-time" but they are pretty accurate nonetheless.
Allegro is in desperate need of threading tools and primitives. Not to be the devil here but SDL has a built-in pthread-like interface. Allegro5 better hurry up. DNF depends on it! I think DOS support is the real problem here holding a lot of this stuff back. Please correct me if i'm wrong.
Allegro has an internal cross platform'ish api.
I would have thought the allegro timers would use the underlying operating system ones (eg: windows highperformance timer). Granted.. none are "real-time" but they are pretty accurate nonetheless.
They do use the high-precision timing methods when possible, but the granularity of the sleep method is the problem. Whether it tries to sleep 1 nanosecond* or one millisecond, the thread will stay down until it gets a timeslice again, which would be in about 10ms. Having the system interrupt your process on time would be the only way to break that.
(*) Sometimes nanosleep will actually busy wait on really small rest values (a few nanoseconds) depending on the system configuration.
So i've gone ahead and wrote a windows/linux/mac timer file that doesn't use allegro. The *nix one uses real-time posix extensions and the windows one uses the multimedia timer set to 1ms accuracy. Both use a semaphore mechanism to wake/sleep.
I've posted it here.
http://www.allegro.cc/forums/thread/594745
edit: no need for windows pthread anymore. THe windows version uses windows own locking mechanism.
I think DOS support is the real problem here holding a lot of this stuff back. Please correct me if i'm wrong.
At your service.
Allegro 5 doesn't have DOS support, unless someone comes along and wants to do the work to try to fit it in somehow.