Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » al_draw_filled_rectangle v. slow compared to al_draw_filled_rectangle

This thread is locked; no one can reply to it. rss feed Print
al_draw_filled_rectangle v. slow compared to al_draw_filled_rectangle
Ionising
Member #14,522
August 2012

I've been adding a minimap to my Isometric game, I first did this by drawing each cell as a simple square using al_draw_filled_rectangle. I then switched to using two al_draw_filled_rectangle calls to draw a diamond shape instead..

The problem I am having is there is a huge FPS difference between the two.. drawing squares results in a sold 60-70 FPS.. drawing two rectangles drops this down to just under 30!

Is this purely because I am making twice as many draw calls? How can I improve this code to be faster?

#SelectExpand
1void CEngine::DrawDiamond( int nCornerTopLeftXPx, 2 int nCornerTopLeftYPx, 3 int nCornerBottomRightXPx, 4 int nCornerBottomRightYPx, 5 int nLineWidth, 6 const RGBValue& rCol, 7 bool fFilled /*= false*/, 8 int nBlendingPercentage /*= 100*/ ) 9{ 10 // Leftmost X, mid Y 11 int nPos1X = nCornerTopLeftXPx; 12 int nPos1Y = ( nCornerBottomRightYPx + nCornerTopLeftYPx ) / 2.0; 13 14 // Topmost Y, mid X 15 int nPos2X = ( nCornerBottomRightXPx + nCornerTopLeftXPx ) / 2.0; 16 int nPos2Y = nCornerTopLeftYPx; 17 18 // Rightmost X, mid Y 19 int nPos3X = nCornerBottomRightXPx; 20 int nPos3Y = ( nCornerBottomRightYPx + nCornerTopLeftYPx ) / 2.0; 21 22 // Bottommost Y, mid X 23 int nPos4X = nPos2X; 24 int nPos4Y = nCornerBottomRightYPx; 25 26 27 DrawTriangle( nPos1X, 28 nPos1Y, 29 nPos2X, 30 nPos2Y, 31 nPos3X, 32 nPos3Y, 33 nLineWidth, 34 rCol, 35 fFilled, 36 nBlendingPercentage ); 37 38 DrawTriangle( nPos1X, 39 nPos1Y, 40 nPos4X, 41 nPos4Y, 42 nPos3X, 43 nPos3Y, 44 nLineWidth, 45 rCol, 46 fFilled, 47 nBlendingPercentage ); 48 49} 50 51void CEngine::DrawRectangle( int nStartX, 52 int nStartY, 53 int nEndX, 54 int nEndY, 55 int nLineWidth, 56 const RGBValue& rCol, 57 bool fFilled /*= false*/, 58 int nBlendingPercentage /*= 100 */ ) 59{ 60 ALLEGRO_COLOR col; 61 62 if( nBlendingPercentage >= 100 ) 63 { 64 col = MakeColourFromRGB( rCol ); 65 } 66 else 67 { 68 col = MakeColourFromRGB( rCol, nBlendingPercentage ); 69 } 70 71 if( fFilled ) 72 { 73 al_draw_filled_rectangle( nStartX, nStartY, nEndX, nEndY, col ); 74 } 75 else 76 { 77 al_draw_rectangle( nStartX, nStartY, nEndX, nEndY, col, nLineWidth ); 78 } 79}

For those interested in how the map is looking here is a WIP shot:

{"name":"mapwip.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/9\/9\/99eea73c7016c301ba7731a646e0b2a6.png","w":768,"h":576,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/9\/9\/99eea73c7016c301ba7731a646e0b2a6"}mapwip.png

Arthur Kalliokoski
Second in Command
February 2005
avatar

I'm just guessing, but I'd say you aren't getting hardware acceleration. Drawing lines is pretty easy even for software, but filling areas takes thousands of memory accesses. Show the code that sets the screen mode please.

They all watch too much MSNBC... they get ideas.

Ionising
Member #14,522
August 2012

Hmm I tried using the non-fill method (al_draw_triangle) and it was even worse performance wise ~8FPS!

{"name":"mapviewwipnonfilled.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/9\/d9e4a4492860f78f469f01ae2beeb73f.png","w":768,"h":576,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/9\/d9e4a4492860f78f469f01ae2beeb73f"}mapviewwipnonfilled.png

This is the screen setting code, I'm afraid it's part of my engine so there is plenty of extra logic in there, but I've tried to cut it down to the essentials for clarity...

#SelectExpand
1 if( ! al_init() ) 2 { 3 GetLogger()->LogError( "al_init failed" ); 4 return false; 5 } 6 7 // 8 // Initialise the display 9 // 10 11 m_nGFXWidth = rConfig.m_nGraphicsWidth; 12 m_nGFXHeight = rConfig.m_nGraphicsHeight; 13 14 // Check the values are not too low or high 15 16 ALLEGRO_DISPLAY_MODE disp_data_minimum; 17 ALLEGRO_DISPLAY_MODE disp_data_maximum; 18 19 al_get_display_mode( 0, &disp_data_minimum ); // First mode 20 al_get_display_mode( al_get_num_display_modes() - 1, &disp_data_maximum ); // Last mode (zero index) 21 22 if( m_nGFXWidth < static_cast< unsigned int >( disp_data_minimum.width ) 23 || m_nGFXHeight < static_cast< unsigned int >( disp_data_minimum.height ) ) 24 { 25 std::string strWarningMessage; 26 utls::FormatString( strWarningMessage, 27 "The supplied resolution (%ux%u) appears to be too low and not supported on this system.", 28 m_nGFXWidth, 29 m_nGFXHeight ); 30 31 GetLogger()->LogWarning( strWarningMessage ); 32 33 // Attempt to continue 34 } 35 else if( m_nGFXWidth > static_cast< unsigned int >( disp_data_maximum.width ) 36 || m_nGFXHeight > static_cast< unsigned int >( disp_data_maximum.height ) ) 37 { 38 std::string strWarningMessage; 39 utls::FormatString( strWarningMessage, 40 "The supplied resolution (%ux%u) appears to be too high and not supported on this system.", 41 m_nGFXWidth, 42 m_nGFXHeight ); 43 44 GetLogger()->LogWarning( strWarningMessage ); 45 46 // Attempt to continue 47 } 48 49 m_fShowSepiaOverlay = rConfig.fShowSepiaOverlay; 50 51 // Enumerate display modes 52 53 if( rConfig.fFullscreen ) 54 { 55 size_t nTotalModes = al_get_num_display_modes(); 56 57 bool fRequestedModeFoundInSupportedModes = false; 58 59 std::vector< std::string > vecModes; 60 61 for( size_t nMode = 0 ; nMode < nTotalModes ; ++nMode ) 62 { 63 ALLEGRO_DISPLAY_MODE disp_data; 64 al_get_display_mode( nMode, &disp_data ); 65 66 if( disp_data.width == static_cast< int >( m_nGFXWidth ) 67 && disp_data.height == static_cast< int >( m_nGFXHeight ) ) 68 { 69 fRequestedModeFoundInSupportedModes = true; 70 } 71 72 std::string strModeBufer; 73 utls::FormatString( strModeBufer, 74 "Mode %u: %ux%u Format %u Refresh Rate %u", 75 nMode, 76 disp_data.width, 77 disp_data.height, 78 disp_data.format, 79 disp_data.refresh_rate ); 80 81 vecModes.push_back( strModeBufer ); 82 } 83 84 if( ! fRequestedModeFoundInSupportedModes ) 85 { 86 std::string strWarningMessage; 87 utls::FormatString( strWarningMessage, 88 "Requested graphics mode [%ux%u] does not appear to be supported by the driver.", 89 m_nGFXWidth, 90 m_nGFXHeight ); 91 92 GetLogger()->LogWarning( strWarningMessage ); 93 MessageBox( NULL, strWarningMessage.c_str(), "Warning", MB_OK ); 94 95 auto it = vecModes.begin(); 96 auto itEnd = vecModes.end(); 97 98 for( ; it != itEnd ; ++it ) 99 { 100 GetLogger()->LogInfo( *it ); 101 } 102 103 // Despite this, try to carry on... 104 } 105 106 if( rConfig.fForceOpenGL ) 107 { 108 al_set_new_display_flags( ALLEGRO_OPENGL | ALLEGRO_FULLSCREEN ); 109 } 110 else 111 { 112 al_set_new_display_flags( ALLEGRO_FULLSCREEN ); 113 } 114 115 m_pDisplay = al_create_display( m_nGFXWidth, m_nGFXHeight ); 116 } 117 else 118 { 119 m_pDisplay = al_create_display( m_nGFXWidth, m_nGFXHeight ); 120 }

pkrcel
Member #14,001
February 2012

Ionising said:

I've been adding a minimap to my Isometric game, I first did this by drawing each cell as a simple square using al_draw_filled_rectangle. I then switched to using two al_draw_filled_rectangle calls to draw a diamond shape instead..

Did you mean al_draw_triangle where I did put bold text? Seems so looking at the code but ain't sure (no native English speaker here).

Is the diamond shape a rotated square? or is it a more generalized quadrilateral?

Either way, there SHOULD be single-call drawing functions (in the 5.1 branch?) of Allegro that could do the trick to get back to the 60-70FPS (just like al_draw_rotated_rectangle maybe?)

Then again, we should find the cause of such a difference...skimming through your code I see nothing obvious but, you know, I am no expert there.

Are you in any position to properly profile the code?

Also, a more efficient solution could be caching the vertexes for each cell in a buffer and the use a single al_draw_prim call?

Sorry, just throwing around ideas for the moment....

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

Ionising
Member #14,522
August 2012

Hey pkrcel, no I was using two calls to al_draw_filled_triangle. I switched to al_draw_triangle this morning to try something based on what Arthur Kalliokoski said but it made things worse..

The diamond shape is two triangles drawn one after the other (top then bottom), but you're right, I could try using a rotated square but I don't see any references to al_draw_rotated_rectangle in the 5.0.x library I'm using. I'd sooner not switch to an unstable branch unless there is no other choice...

I really don't understand why there would be such a difference in performance between these calls, hence the reason for this thread :) for reference I'm using Allegro 5.0.10. I can profile my code, but not the allegro library properly.

I think I'm just going to load a suitably sized sprite and tint it to the correct colour as a work around for now, but it seems crazy to go through so much 'heavy' lifting to draw a primitive ;D

pkrcel
Member #14,001
February 2012

Ionising said:

The diamond shape is two triangles drawn one after the other (top then bottom), but you're right, I could try using a rotated square but I don't see any references to al_draw_rotated_rectangle in the 5.0.x library I'm using. I'd sooner not switch to an unstable branch unless there is no other choice...

Just a quick post...I confused rectangles with bitmaps!!!! shame on me. :P

EDIT:
in the 5.1 branch there is al_draw_filled_polygon which should do the trick.

Anyway as I see it is a convenient wrapper around al_draw_prim, which could be tougher to use but is available in 5.0 (I guess? there is no "since" in the docs for this).

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

Ionising
Member #14,522
August 2012

Thanks for the prod in the right direction! I dug around and figured out how to make al_draw_prim do what I wanted. Getting about 50 FPS now which is almost twice what two triangle calls was for the same result.. going to fiddle a bit more but quite pleased with this. :)

EDIT: Here is the optimised code:

#SelectExpand
1void CSepiaEngine::DrawDiamond( int nCornerTopLeftXPx, 2 int nCornerTopLeftYPx, 3 int nCornerBottomRightXPx, 4 int nCornerBottomRightYPx, 5 const RGBValue& rCol, 6 bool fFilled /*= false*/, 7 int nBlendingPercentage /*= 100*/ ) 8{ 9 10 // Leftmost X, mid Y 11 int nPos1X = nCornerTopLeftXPx; 12 int nPos1Y = ( nCornerBottomRightYPx + nCornerTopLeftYPx ) / 2.0; 13 14 // Topmost Y, mid X 15 int nPos2X = ( nCornerBottomRightXPx + nCornerTopLeftXPx ) / 2.0; 16 int nPos2Y = nCornerTopLeftYPx; 17 18 // Rightmost X, mid Y 19 int nPos3X = nCornerBottomRightXPx; 20 int nPos3Y = ( nCornerBottomRightYPx + nCornerTopLeftYPx ) / 2.0; 21 22 // Bottommost Y, mid X 23 int nPos4X = nPos2X; 24 int nPos4Y = nCornerBottomRightYPx; 25 26 ALLEGRO_VERTEX vertices[4]; 27 28 vertices[0].x = nPos1X; 29 vertices[0].y = nPos1Y; 30 vertices[0].z = 0; 31 vertices[0].u = vertices[0].x; 32 vertices[0].v = vertices[0].y; 33 vertices[0].color = MakeColourFromRGB( rCol, nBlendingPercentage ); 34 35 vertices[1].x = nPos2X; 36 vertices[1].y = nPos2Y; 37 vertices[1].z = 0; 38 vertices[1].u = vertices[1].x; 39 vertices[1].v = vertices[1].y; 40 vertices[1].color = MakeColourFromRGB( rCol, nBlendingPercentage ); 41 42 vertices[2].x = nPos3X; 43 vertices[2].y = nPos3Y; 44 vertices[2].z = 0; 45 vertices[2].u = vertices[2].x; 46 vertices[2].v = vertices[2].y; 47 vertices[2].color = MakeColourFromRGB( rCol, nBlendingPercentage ); 48 49 vertices[3].x = nPos4X; 50 vertices[3].y = nPos4Y; 51 vertices[3].z = 0; 52 vertices[3].u = vertices[3].x; 53 vertices[3].v = vertices[3].y; 54 vertices[3].color = MakeColourFromRGB( rCol, nBlendingPercentage ); 55 56 // More info on types: https://www.allegro.cc/manual/5/allegro_prim_type 57 ALLEGRO_PRIM_TYPE eDrawingType = fFilled ? ALLEGRO_PRIM_TRIANGLE_FAN : ALLEGRO_PRIM_LINE_STRIP; 58 59 al_draw_prim( vertices, 60 nullptr, 61 0, // No texture 62 0, 63 4, 64 eDrawingType ); 65 66}

Mark Oates
Member #1,146
March 2001
avatar

You will also find that drawing bitmaps will be substantially faster than drawing (filled or not) primitives. You would get a speed boost if you made square bitmaps and drew those instead.

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

pkrcel
Member #14,001
February 2012

Ionising said:

Thanks for the prod in the right direction! I dug around and figured out how to make al_draw_prim do what I wanted. Getting about 50 FPS now which is almost twice what two triangle calls was for the same result.. going to fiddle a bit more but quite pleased with this.

There are countless discussions about primitives having overhead, not really sure if unfounded or not, anyway it makes sense that you group all calls to al_draw_prim building a list of vertexes and making a single draw call....call it an exercise, and if you succeed pls share results with me as I have no Idea of what I'm talking about ;D

You will also find that drawing bitmaps will be substantially faster than drawing (filled or not) primitives. You would get a speed boost if you made square bitmaps and drew those instead.

Wouldn't this be true only if holding bitmap draws (al_hold_bitmap_drawing)?

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

Mark Oates
Member #1,146
March 2001
avatar

pkrcel said:

Wouldn't this be true only if holding bitmap draws (al_hold_bitmap_drawing)?

I've never actually run any tests with al_hold_bitmap_drawing, but I can tell you that you will get a faster render with bitmaps whether or not you defer your bitmap drawing.

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

pkrcel
Member #14,001
February 2012

Recent discussions seem to indicate that there is some controversy about that...

Batching a single al_draw_prim (with either textures or not) seems still the most efficient way to draw a (somewhat big) bunch of (maybe non overlapping) things...

Defer drawing seems to have similar effects as batching draw calls in al_draw_prim.

I found it extremely interesting but I am unable to test that right-no, actually I am unable to devote any serious time to Allegro or programming (aside participating in the forums not to rot my already abysmal knowledge >:( )

EDIT:
Hey ionising thaks for posting the draw code...interesting...

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

Slartibartfast
Member #8,789
June 2007
avatar

I've never actually run any tests with al_hold_bitmap_drawing, but I can tell you that you will get a faster render with bitmaps whether or not you defer your bitmap drawing.

Does using the al_draw_(primitive) functions create a vertex buffer, fill it, pass it on and the destroy it, and that is the reason why bitmap drawing is faster even without deferred drawing?
If so then I think using al_draw_prim won't help (since you are passing vertex data from your memory), but using al_draw_vertex_buffer should speed things up considerably, though I see no simple way to control where the buffer will be drawn other then setting the appropriate transformation before drawing and then restoring it.

Go to: