Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance boost for SDL ASCII drawing using color modulated textures #27566

Merged
merged 8 commits into from
Jan 20, 2019
48 changes: 42 additions & 6 deletions src/cata_tiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#include "monstergenerator.h"
#include "mtype.h"
#include "npc.h"
#include "options.h"
#include "output.h"
#include "overlay_ordering.h"
#include "path_info.h"
Expand Down Expand Up @@ -1022,8 +1021,7 @@ void cata_tiles::draw( int destx, int desty, const tripoint &center, int width,
"SDL_RenderSetClipRect failed" );

//fill render area with black to prevent artifacts where no new pixels are drawn
SetRenderDrawColor( renderer, 0, 0, 0, 255 );
RenderFillRect( renderer, &clipRect );
handle_draw_rect( renderer, clipRect, 0, 0, 0 );
}

int posx = center.x;
Expand Down Expand Up @@ -1337,7 +1335,7 @@ void cata_tiles::process_minimap_cache_updates()
rectangle.x = p.x * minimap_tile_size.x;
rectangle.y = p.y * minimap_tile_size.y;

RenderFillRect( renderer, &rectangle );
handle_draw_rect( renderer, rectangle, c.r, c.g, c.b );
}
}
mcp.second.update_list.clear();
Expand Down Expand Up @@ -2245,8 +2243,7 @@ bool cata_tiles::draw_terrain_below( const tripoint &p, lit_level /*ll*/, int &/
if( tile_iso ) {
belowRect.y += tile_height / 8;
}
SetRenderDrawColor( renderer, tercol.r, tercol.g, tercol.b, 255 );
RenderFillRect( renderer, &belowRect );
handle_draw_rect( renderer, belowRect, tercol.r, tercol.g, tercol.b );

return true;
}
Expand Down Expand Up @@ -3195,4 +3192,43 @@ void cata_tiles::tile_loading_report( const arraytype &array, int array_length,
return v->id;
}, label, prefix );
}

inline void cata_tiles::handle_draw_rect( const SDL_Renderer_Ptr &renderer, const SDL_Rect &rect,
Uint32 r, Uint32 g, Uint32 b )
{
if( alt_rect_tex_enabled ) {
SetTextureColorMod( alt_rect_tex, r, g, b );
RenderCopy( renderer, alt_rect_tex, NULL, &rect );
} else {
SetRenderDrawColor( renderer, r, g, b, 255 );
RenderFillRect( renderer, &rect );
}
}

std::vector<options_manager::id_and_option> cata_tiles::build_renderer_list()
{
std::vector<options_manager::id_and_option> renderer_names;
std::vector<options_manager::id_and_option> default_renderer_names = {
#if (defined TILES)
# if (defined _WIN32 || defined WINDOWS)
{ "direct3d", translate_marker( "direct3d" ) },
# endif
{ "opengl", translate_marker( "opengl" ) },
{ "opengles2", translate_marker( "opengles2" ) },
#endif
{ "software", translate_marker( "software" ) }
};

int numRenderDrivers = SDL_GetNumRenderDrivers();
DebugLog( D_INFO, DC_ALL ) << "Number of render drivers on your system: " << numRenderDrivers;
for( int ii = 0; ii < numRenderDrivers; ii++ ) {
SDL_RendererInfo ri;
SDL_GetRenderDriverInfo( ii, &ri );
DebugLog( D_INFO, DC_ALL ) << "Render driver: " << ii << "/" << ri.name;
renderer_names.emplace_back( ri.name, ri.name );
}

return renderer_names.empty() ? default_renderer_names : renderer_names;
}

#endif // SDL_TILES
12 changes: 11 additions & 1 deletion src/cata_tiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
#include <memory>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <vector>
#include <unordered_map>

#include "sdl_wrappers.h"
#include "animation.h"
#include "lightmap.h"
#include "line.h"
#include "options.h"
#include "game_constants.h"
#include "weather.h"
#include "enums.h"
Expand Down Expand Up @@ -92,6 +93,11 @@ class texture
}
};

extern SDL_Texture_Ptr alt_rect_tex;
extern bool alt_rect_tex_enabled;
extern void draw_alt_rect( const SDL_Renderer_Ptr &renderer, const SDL_Rect &rect,
Uint32 r, Uint32 g, Uint32 b );

struct pixel {
int r;
int g;
Expand Down Expand Up @@ -525,6 +531,7 @@ class cata_tiles
}
void do_tile_loading_report();
point player_to_screen( int x, int y ) const;
static std::vector<options_manager::id_and_option> build_renderer_list();
protected:
template <typename maptype>
void tile_loading_report( const maptype &tiletypemap, const std::string &label,
Expand Down Expand Up @@ -640,6 +647,9 @@ class cata_tiles
//place all submaps on this texture before rendering to screen
//replaces clipping rectangle usage while SDL still has a flipped y-coordinate bug
SDL_Texture_Ptr main_minimap_tex;
// SDL_RenderFillRect replacement handler
void handle_draw_rect( const SDL_Renderer_Ptr &renderer, const SDL_Rect &rect,
Uint32 r, Uint32 g, Uint32 b );
};

#endif
32 changes: 32 additions & 0 deletions src/debug_menu.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "debug_menu.h"

#include <algorithm>
#include <chrono>
#include <vector>

#include "action.h"
Expand All @@ -11,6 +12,7 @@
#include "morale_types.h"
#include "npc.h"
#include "npc_class.h"
#include "options.h"
#include "output.h"
#include "overmap.h"
#include "overmap_ui.h"
Expand Down Expand Up @@ -607,4 +609,34 @@ void mission_debug::edit_mission( mission &m )
}
}

void draw_benchmark( const int max_difference )
{
// call the draw procedure as many times as possible in max_difference milliseconds
auto start_tick = std::chrono::steady_clock::now();
auto end_tick = std::chrono::steady_clock::now();
long difference = 0;
int draw_counter = 0;
while( true ) {
end_tick = std::chrono::steady_clock::now();
difference = std::chrono::duration_cast<std::chrono::milliseconds>( end_tick - start_tick ).count();
if( difference >= max_difference ) {
break;
}
g->draw();
draw_counter++;
}

DebugLog( D_INFO, DC_ALL ) << "Draw benchmark:\n" <<
"\n| USE_TILES | RENDERER | FRAMEBUFFER_ACCEL | USE_COLOR_MODULATED_TEXTURES | FPS |" <<
"\n|:---:|:---:|:---:|:---:|:---:|\n| " <<
get_option<bool>( "USE_TILES" ) << " | " <<
get_option<std::string>( "RENDERER" ) << " | " <<
get_option<bool>( "FRAMEBUFFER_ACCEL" ) << " | " <<
get_option<bool>( "USE_COLOR_MODULATED_TEXTURES" ) << " | " <<
int( 1000.0 * draw_counter / ( double )difference ) << " |\n";

add_msg( m_info, _( "Drew %d times in %.3f seconds. (%.3f fps average)" ), draw_counter,
difference / 1000.0, 1000.0 * draw_counter / ( double )difference );
}

}
1 change: 1 addition & 0 deletions src/debug_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ void wishmonster( const cata::optional<tripoint> &p );
void wishmutate( player *p );
void wishskill( player *p );
void mutation_wish();
void draw_benchmark( const int max_difference );

class mission_debug;

Expand Down
25 changes: 7 additions & 18 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2983,7 +2983,7 @@ void game::debug()
_( "Set automove route" ), // 27
_( "Show mutation category levels" ), // 28
_( "Overmap editor" ), // 29
_( "Draw benchmark (5 seconds)" ), // 30
_( "Draw benchmark (X seconds)" ), // 30
_( "Teleport - Adjacent overmap" ), // 31
_( "Test trait group" ), // 32
_( "Show debug message" ), // 33
Expand Down Expand Up @@ -3315,23 +3315,12 @@ void game::debug()
break;

case 30: {
// call the draw procedure as many times as possible in 5 seconds
auto start_tick = std::chrono::steady_clock::now();
auto end_tick = std::chrono::steady_clock::now();
long difference = 0;
int draw_counter = 0;
while( true ) {
end_tick = std::chrono::steady_clock::now();
difference = std::chrono::duration_cast<std::chrono::milliseconds>( end_tick -
start_tick ).count();
if( difference >= 5000 ) {
break;
}
draw();
draw_counter++;
}
add_msg( m_info, _( "Drew %d times in %.3f seconds. (%.3f fps average)" ), draw_counter,
difference / 1000.0, 1000.0 * draw_counter / static_cast<double>( difference ) );
const int ms = string_input_popup()
.title( _( "Enter benchmark length (in milliseconds):" ) )
.width( 20 )
.text( "5000" )
.query_int();
debug_menu::draw_benchmark( ms );
}
break;

Expand Down
59 changes: 52 additions & 7 deletions src/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,16 +356,42 @@ void options_manager::add( const std::string &sNameIn, const std::string &sPageI
options[sNameIn] = thisOpt;
}

void options_manager::cOpt::setPrerequisite( const std::string &sOption )
void options_manager::cOpt::setPrerequisites( const std::string &sOption,
const std::vector<std::string> &sAllowedValues )
{
if( !get_options().has_option( sOption ) ) {
static const std::vector<std::string> &sSupportedTypes = { "bool", "string", "string_select", "string_input" };
const bool hasOption = get_options().has_option( sOption );
if( !hasOption ) {
debugmsg( "setPrerequisite: unknown option %s", sType.c_str() );
return;
}

const cOpt &existingOption = get_options().get_option( sOption );
const std::string &existingOptionType = existingOption.getType();
bool isOfSupportType = false;
for( const std::string &sSupportedType : sSupportedTypes ) {
if( existingOptionType == sSupportedType ) {
isOfSupportType = true;
break;
}
}

} else if( get_options().get_option( sOption ).getType() != "bool" ) {
if( !isOfSupportType ) {
debugmsg( "setPrerequisite: option %s not of type bool", sType.c_str() );
return;
}

sPrerequisite = sOption;
if( !sAllowedValues.empty() ) {
const std::string &existingOptionValue = existingOption.getValue();
for( const std::string &sAllowedValue : sAllowedValues ) {
if( existingOptionValue == sAllowedValue ) {
sPrerequisite = sOption;
break;
}
}
} else {
sPrerequisite = sOption;
}
}

std::string options_manager::cOpt::getPrerequisite() const
Expand Down Expand Up @@ -1514,22 +1540,41 @@ void options_manager::add_options_graphics()
);
#endif

#ifndef __ANDROID__
add( "RENDERER", "graphics", translate_marker( "Renderer" ),
translate_marker( "Set which renderer to use. Requires restart." ),
# ifndef TILES
// No renderer selection in non-TILES mode
{ { "software", translate_marker( "software" ) }
},
# else
cata_tiles::build_renderer_list(),
# endif
"software", COPT_CURSES_HIDE );
#else
add( "SOFTWARE_RENDERING", "graphics", translate_marker( "Software rendering" ),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should get rid of this option since it's redundant with renderer=software

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

translate_marker( "Use software renderer instead of graphics card acceleration. Requires restart." ),
#ifdef __ANDROID__
android_get_default_setting( "Software rendering", false ),
COPT_CURSES_HIDE // take default setting from pre-game settings screen - important as both software + hardware rendering have issues with specific devices
#else
false, COPT_CURSES_HIDE
#endif
);
#endif

add( "FRAMEBUFFER_ACCEL", "graphics", translate_marker( "Software framebuffer acceleration" ),
translate_marker( "Use hardware acceleration for the framebuffer when using software rendering. Requires restart." ),
false, COPT_CURSES_HIDE
);

#ifdef __ANDROID__
get_option( "FRAMEBUFFER_ACCEL" ).setPrerequisite( "SOFTWARE_RENDERING" );
#else
get_option( "FRAMEBUFFER_ACCEL" ).setPrerequisite( "RENDERER", "software" );
#endif

add( "USE_COLOR_MODULATED_TEXTURES", "graphics", translate_marker( "Use color modulated textures" ),
translate_marker( "If true, tries to use color modulated textures to speed-up ASCII drawing. Requires restart." ),
false, COPT_CURSES_HIDE
);

add( "SCALING_MODE", "graphics", translate_marker( "Scaling mode" ),
translate_marker( "Sets the scaling mode, 'none' ( default ) displays at the game's native resolution, 'nearest' uses low-quality but fast scaling, and 'linear' provides high-quality scaling." ),
Expand Down
5 changes: 4 additions & 1 deletion src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ class options_manager
return !operator==( rhs );
}

void setPrerequisite( const std::string &sOption );
void setPrerequisites( const std::string &sOption, const std::vector<std::string> &sAllowedValues );
void setPrerequisite( const std::string &sOption, const std::string &sAllowedValue = "" ) {
setPrerequisites( sOption, { sAllowedValue } );
}
std::string getPrerequisite() const;
bool hasPrerequisite() const;

Expand Down
20 changes: 20 additions & 0 deletions src/sdl_wrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,25 @@ void RenderFillRect( const SDL_Renderer_Ptr &renderer, const SDL_Rect *const rec
printErrorIf( SDL_RenderFillRect( renderer.get(), rect ) != 0, "SDL_RenderFillRect failed" );
}

void FillRect( const SDL_Surface_Ptr &surface, const SDL_Rect *const rect, Uint32 color )
{
if( !surface ) {
dbg( D_ERROR ) << "Tried to use a null surface";
return;
}
printErrorIf( SDL_FillRect( surface.get(), rect, color ) != 0, "SDL_FillRect failed" );
}

bool SetTextureColorMod( const SDL_Texture_Ptr &texture, Uint32 r, Uint32 g, Uint32 b )
{
if( !texture ) {
dbg( D_ERROR ) << "Tried to use a null texture";
return true;
}
return printErrorIf( SDL_SetTextureColorMod( texture.get(), r, g, b ) != 0,
"SDL_SetTextureColorMod failed" );
}

void SetRenderDrawBlendMode( const SDL_Renderer_Ptr &renderer, const SDL_BlendMode blendMode )
{
if( !renderer ) {
Expand Down Expand Up @@ -133,4 +152,5 @@ SDL_Surface_Ptr CreateRGBSurface( const Uint32 flags, const int width, const int
throwErrorIf( !surface, "Failed to create surface" );
return surface;
}

#endif
2 changes: 2 additions & 0 deletions src/sdl_wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ SDL_Texture_Ptr CreateTextureFromSurface( const SDL_Renderer_Ptr &renderer,
const SDL_Surface_Ptr &surface );
void SetRenderDrawColor( const SDL_Renderer_Ptr &renderer, Uint8 r, Uint8 g, Uint8 b, Uint8 a );
void RenderFillRect( const SDL_Renderer_Ptr &renderer, const SDL_Rect *rect );
void FillRect( const SDL_Surface_Ptr &surface, const SDL_Rect *rect, Uint32 color );
bool SetTextureColorMod( const SDL_Texture_Ptr &texture, Uint32 r, Uint32 g, Uint32 b );
void SetRenderDrawBlendMode( const SDL_Renderer_Ptr &renderer, SDL_BlendMode blendMode );
SDL_Surface_Ptr load_image( const char *path );
void SetRenderTarget( const SDL_Renderer_Ptr &renderer, const SDL_Texture_Ptr &texture );
Expand Down
Loading