Improve sheet rendering performance

- Cache font markup parsing
- Cache SCH_SYMBOL::GetOrientation
- Don't construct new strings each time in GetDefaultFieldName
This commit is contained in:
Jon Evans 2023-03-05 12:25:36 -05:00
parent 5b43dc4e7b
commit 7b3fd2113c
8 changed files with 149 additions and 15 deletions

View File

@ -24,6 +24,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <list>
#include <mutex>
#include <unordered_map>
#include <wx/font.h>
#include <string_utils.h>
#include <gal/graphics_abstraction_layer.h>
@ -47,6 +51,75 @@ FONT* FONT::s_defaultFont = nullptr;
std::map< std::tuple<wxString, bool, bool>, FONT*> FONT::s_fontMap;
class MARKUP_CACHE
{
public:
struct ENTRY
{
std::string source;
std::unique_ptr<MARKUP::NODE> root;
};
typedef std::pair<wxString, ENTRY> CACHE_ENTRY;
MARKUP_CACHE( size_t aMaxSize ) :
m_maxSize( aMaxSize )
{
}
ENTRY& Put( const CACHE_ENTRY::first_type& aQuery, ENTRY&& aResult )
{
auto it = m_cache.find( aQuery );
m_cacheMru.emplace_front( CACHE_ENTRY( aQuery, std::move( aResult ) ) );
if( it != m_cache.end() )
{
m_cacheMru.erase( it->second );
m_cache.erase( it );
}
m_cache[aQuery] = m_cacheMru.begin();
if( m_cache.size() > m_maxSize )
{
auto last = m_cacheMru.end();
last--;
m_cache.erase( last->first );
m_cacheMru.pop_back();
}
return m_cacheMru.begin()->second;
}
ENTRY* Get( const CACHE_ENTRY::first_type& aQuery )
{
auto it = m_cache.find( aQuery );
if( it == m_cache.end() )
return nullptr;
m_cacheMru.splice( m_cacheMru.begin(), m_cacheMru, it->second );
return &m_cacheMru.begin()->second;
}
void Clear()
{
m_cacheMru.clear();
m_cache.clear();
}
private:
size_t m_maxSize;
std::list<CACHE_ENTRY> m_cacheMru;
std::unordered_map<wxString, std::list<CACHE_ENTRY>::iterator> m_cache;
};
static MARKUP_CACHE s_markupCache( 1024 );
static std::mutex s_markupCacheMutex;
FONT::FONT()
{
@ -186,7 +259,7 @@ void FONT::Draw( KIGFX::GAL* aGal, const wxString& aText, const VECTOR2I& aPosit
* @return position of cursor for drawing next substring
*/
VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const std::unique_ptr<MARKUP::NODE>& aNode, const VECTOR2I& aPosition,
const MARKUP::NODE* aNode, const VECTOR2I& aPosition,
const KIFONT::FONT* aFont, const VECTOR2I& aSize, const EDA_ANGLE& aAngle,
bool aMirror, const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle )
{
@ -221,8 +294,8 @@ VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>* a
for( const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
{
nextPosition = drawMarkup( aBoundingBox, aGlyphs, child, nextPosition, aFont, aSize,
aAngle, aMirror, aOrigin, textStyle );
nextPosition = drawMarkup( aBoundingBox, aGlyphs, child.get(), nextPosition, aFont,
aSize, aAngle, aMirror, aOrigin, textStyle );
}
}
@ -235,11 +308,24 @@ VECTOR2I FONT::drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYP
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const
{
MARKUP::MARKUP_PARSER markupParser( TO_UTF8( aText ) );
std::unique_ptr<MARKUP::NODE> root = markupParser.Parse();
std::lock_guard<std::mutex> lock( s_markupCacheMutex );
return ::drawMarkup( aBoundingBox, aGlyphs, root, aPosition, this, aSize, aAngle, aMirror,
aOrigin, aTextStyle );
MARKUP_CACHE::ENTRY* markup = s_markupCache.Get( aText );
if( !markup || !markup->root )
{
MARKUP_CACHE::ENTRY& cached = s_markupCache.Put( aText, {} );
cached.source = TO_UTF8( aText );
MARKUP::MARKUP_PARSER markupParser( &cached.source );
cached.root = markupParser.Parse();
markup = &cached;
}
wxASSERT( markup && markup->root );
return ::drawMarkup( aBoundingBox, aGlyphs, markup->root.get(), aPosition, this, aSize, aAngle,
aMirror, aOrigin, aTextStyle );
}

View File

@ -30,7 +30,13 @@ std::unique_ptr<NODE> MARKUP_PARSER::Parse()
{
try
{
auto root = parse_tree::parse<MARKUP::grammar, MARKUP::NODE, MARKUP::selector>( in );
std::unique_ptr<NODE> root;
if( mem_in )
root = parse_tree::parse<MARKUP::grammar, MARKUP::NODE, MARKUP::selector>( *mem_in );
else
root = parse_tree::parse<MARKUP::grammar, MARKUP::NODE, MARKUP::selector>( *in );
return root;
}
catch ( tao::pegtl::parse_error& )

View File

@ -36,16 +36,21 @@ using namespace TFIELD_T;
#define FOOTPRINT_CANONICAL "Footprint"
#define DATASHEET_CANONICAL "Datasheet"
static wxString s_CanonicalReference( REFERENCE_CANONICAL );
static wxString s_CanonicalValue( VALUE_CANONICAL );
static wxString s_CanonicalFootprint( FOOTPRINT_CANONICAL );
static wxString s_CanonicalDatasheet( DATASHEET_CANONICAL );
const wxString TEMPLATE_FIELDNAME::GetDefaultFieldName( int aFieldNdx, bool aTranslateForHI )
{
if( !aTranslateForHI )
{
switch( aFieldNdx )
{
case REFERENCE_FIELD: return REFERENCE_CANONICAL; // The symbol reference, R1, C1, etc.
case VALUE_FIELD: return VALUE_CANONICAL; // The symbol value
case FOOTPRINT_FIELD: return FOOTPRINT_CANONICAL; // The footprint for use with Pcbnew
case DATASHEET_FIELD: return DATASHEET_CANONICAL; // Link to a datasheet for symbol
case REFERENCE_FIELD: return s_CanonicalReference; // The symbol reference, R1, C1, etc.
case VALUE_FIELD: return s_CanonicalValue; // The symbol value
case FOOTPRINT_FIELD: return s_CanonicalFootprint; // The footprint for use with Pcbnew
case DATASHEET_FIELD: return s_CanonicalDatasheet; // Link to a datasheet for symbol
default: return wxString::Format( wxT( "Field%d" ), aFieldNdx );
}
}

View File

@ -22,6 +22,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <hash.h>
#include <macros.h>
#include <trigo.h>
#include <transform.h>
@ -124,3 +125,9 @@ bool TRANSFORM::MapAngles( EDA_ANGLE* aAngle1, EDA_ANGLE* aAngle2 ) const
}
size_t std::hash<TRANSFORM>::operator()( const TRANSFORM& s ) const
{
size_t seed = std::hash<int>{}( s.x1 );
hash_combine( seed, s.y1, s.x2, s.y2 );
return seed;
}

View File

@ -44,6 +44,9 @@
#include "plotters/plotter.h"
std::unordered_map<TRANSFORM, int> SCH_SYMBOL::s_transformToOrientationCache;
/**
* Convert a wxString to UTF8 and replace any control characters with a ~,
* where a control character is one of the first ASCII values up to ' ' 32d.
@ -1535,6 +1538,13 @@ void SCH_SYMBOL::SetOrientation( int aOrientation )
int SCH_SYMBOL::GetOrientation() const
{
/*
* This is slow, but also a bizarre algorithm. I don't feel like unteasing the algorithm right
* now, so let's just cache it for the moment.
*/
if( s_transformToOrientationCache.count( m_transform ) )
return s_transformToOrientationCache.at( m_transform );
int rotate_values[] =
{
SYM_ORIENT_0,
@ -1560,8 +1570,11 @@ int SCH_SYMBOL::GetOrientation() const
temp.SetOrientation( type_rotate );
if( transform == temp.GetTransform() )
{
s_transformToOrientationCache[m_transform] = type_rotate;
return type_rotate;
}
}
// Error: orientation not found in list (should not happen)
wxFAIL_MSG( "Schematic symbol orientation matrix internal error." );

View File

@ -798,6 +798,9 @@ private:
// Defines the hierarchical path and reference of the symbol. This allows support
// for multiple references to a single sub-sheet.
std::vector<SCH_SYMBOL_INSTANCE> m_instanceReferences;
/// @see SCH_SYMBOL::GetOrientation
static std::unordered_map<TRANSFORM, int> s_transformToOrientationCache;
};
#endif /* __SYMBOL_H__ */

View File

@ -97,13 +97,20 @@ class MARKUP_PARSER
{
public:
MARKUP_PARSER( const std::string& source ) :
in( source, "from_input" )
in( std::make_unique<string_input<>>( source, "from_input" ) ),
mem_in()
{}
MARKUP_PARSER( const std::string* source ) :
in(),
mem_in( std::make_unique<memory_input<>>( *source, "from_input" ) )
{}
std::unique_ptr<NODE> Parse();
private:
string_input<> in;
std::unique_ptr<string_input<>> in;
std::unique_ptr<memory_input<>> mem_in;
};
} // namespace MARKUP

View File

@ -98,5 +98,12 @@ public:
bool MapAngles( EDA_ANGLE* aAngle1, EDA_ANGLE* aAngle2 ) const;
};
namespace std
{
template <> struct hash<TRANSFORM>
{
size_t operator() ( const TRANSFORM& k ) const;
};
}
#endif // _TRANSFORM_H_