Allegro.cc - Online Community
Post Reply

Allegro.cc Forums » Allegro Development » al_set_target_bitmap slow (in release mode only)

rss feed Print
al_set_target_bitmap slow (in release mode only)
AciusPrime
Member #16,741
September 2017

Hi. I am using Allegro 5.2.2.1 (I think, not sure how to check) from the Nuget packages. I'm not set up with a dev build of Allegro yet, so I can't be too specific about the cause of this problem yet. I figured I'd start a thread and see if anyone had any ideas first.

I am trying to track down some performance issues. I'm running in windowed mode (not full-screen windowed) under Windows 10. I have an "overlay" bitmap that I regenerate every frame and then render on top of everything else.

My performance issue was tracked down to some code that reads:

ALLEGRO_BITMAP *former_bitmap = al_get_target_bitmap();
al_set_target_bitmap( mSurface ); //****** THIS IS CRAZY SLOW ******
al_clear_to_color( al_map_rgba( 0, 0, 0, 0 ) );
al_set_target_bitmap( former_bitmap );

The only line that is running slowly is that first al_set_target_bitmap call. Some initial Googling suggested that I should create mSurface with ALLEGRO_NO_PRESERVE_TEXTURE (https://www.allegro.cc/forums/thread/616223), but it doesn't make any noticeable difference to the performance. I did call al_get_bitmap_flags after creating it to verify that ALLEGRO_NO_PRESERVE_TEXTURE was set--it was.

I stuck a high-resolution timer in there, and it is showing that this single line of code takes about 0.004 seconds in debug mode (MSVC), and about 0.0146 seconds in release mode. You are reading that correctly: debug mode is much faster than release mode ???. I get the same behavior on both 32- and 64-bit builds, so it's not that either.

Can anyone suggest what I should try next?

EDIT: The problem goes away if I remove ALLEGRO_OPENGL from my initial call to al_set_new_display_flags (previously: ALLEGRO_OPENGL | ALLEGRO_RESIZABLE | ALLEGRO_WINDOWED; much faster when using: ALLEGRO_WINDOWED | ALLEGRO_RESIZABLE). This seems to be an OpenGL-only issue.

beoran
Member #12,636
March 2011

Perhaps you could trace into al_set_target_bitmap with a debugger or performance measurement tool and see what is going on? Probably Allegro inadvertently does something different in debug mode. Also, are you making a software or hardware bitmap? The former may be slow.

Also, is it really necessary to draw to the intermediate bitmap every frame? I would only use intermediate bitmaps for prerendering or caching during loading. Then draw directly to the backbuffer of the display using the painters algorithm.

https://en.m.wikipedia.org/wiki/Painter%27s_algorithm

AciusPrime
Member #16,741
September 2017

Thanks for the thoughts. Unfortunately, I would need to have a development version of Allegro to trace into al_set_target_bitmap, and all I have is the nuget binaries. It would take me a couple of hours to switch to a development build, so I'm not sure when I'm going to get around to it.

While I probably don't need to redraw the entire intermediate bitmap every frame, drawing at least one small thing to it every frame is going to be very common (it's a kind of heads-up-display overlay that lies on top of the play area). There are certainly other ways to accomplish this, but the main point of interest here is the bizarre "worse performance in release mode" issue. That shouldn't happen [almost] no matter what I'm doing. Even if I rewrote this particular piece of code, there are lots of reasons that I might want to draw to other bitmaps every frame--or even every few frames. But that kind of performance hit for switching render targets makes the whole system unusable for real time rendering, unfortunately.

beoran
Member #12,636
March 2011

Well, I agree that this is probably a performance problem. I also can't imagine why switching target bitmaps should be this slow.

Could you perhaps make a minimal test program that demonstrates the issue and post that on the Allegro github issue tracker? We are a bit short staffed these days so your best bet is still to try to debug it with the debug version of Allegro, but if it is in the issue tracker then we will be reminded to take a look at it.

SiegeLord
Member #7,827
October 2006
avatar

I tried to reproduce this, but couldn't on my Win7. Here's the code I used:

al_set_new_display_flags(ALLEGRO_OPENGL);
auto* d = al_create_display(800, 600);
auto *b = al_create_bitmap(200, 200);
for (int i = 0; i < 10000; i++) {
  al_set_target_bitmap(b);
}
double start = al_get_time();
for (int i = 0; i < 1000; i++) {
  al_set_target_bitmap(b);
  al_clear_to_color(al_map_rgb_f(1.0, 0.0, 0.5));
  al_set_target_bitmap(al_get_backbuffer(d));
}
printf("taken: %f\n", al_get_time() - start);
al_rest(5.0);

That takes about 0.02 secs on my system. I don't actually have a real install of Win10, but I guess that's the next step to try.

Are you using 'real' OpenGL drivers (those vendored by the GPU manufacturer?).

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

AciusPrime
Member #16,741
September 2017

Thanks for chasing this down, SiegeLord. I tried your code snippet on my machine and saw no performance problems (0.003 ms typically). I'm going to spend some time playing with it to see if I can reproduce the bad performance in a simple snippet.

EDIT: And here's my repro (this is a Windows Console app):

int main( int, char ** )
{
	al_init();
	al_set_new_display_flags( ALLEGRO_OPENGL );
	auto *d = al_create_display( 640, 480 );
	auto *b = al_create_bitmap( 200, 200 );
	double start = al_get_time();
	for( int i = 0; i < 100; i++ ) {
		al_set_target_bitmap( b );
		al_flip_display();
	}
	printf( "taken: %f\n", al_get_time() - start );
	al_rest( 3.0 );
	return 0;
}

If you comment out "al_set_new_display_flags( ALLEGRO_OPENGL );" then my time drops from 1.6s to 0.003s.

Elias
Member #358
May 2000

al_flip_display() waits for vsync with OpenGL, so each call will take 0.0167 seconds.

--
"Either help out or stop whining" - Evert

AciusPrime
Member #16,741
September 2017

That's probably related, but there's something really funny going on here. The wait doesn't occur inside of al_flip_display(). For example:

double set_target_elapsed = 0.0;
double flip_display_elapsed = 0.0;
for( int i = 0; i < 100; i++ ) {
	double start = al_get_time();
	al_set_target_bitmap( b );
	set_target_elapsed += al_get_time() - start;
	start = al_get_time();
	al_flip_display();
	flip_display_elapsed += al_get_time() - start;
}
printf( "taken: %f, %f\n", set_target_elapsed, flip_display_elapsed );

This prints (approximately):

taken: 1.63s, 0.005s

That is a really good point about the 1/60s wait for an OpenGL display, but why does the wait happen while al_set_target_bitmap is on the stack? If I comment out the al_set_target_bitmap() line, then the entirety of the wait moves into al_flip_display().

On a positive note, that means that the performance penalty might just be a red herring. It does seem to impact frame rate, though, so it'd be nice to know what's going on here.

Post Reply
Go to: