Reduce allocations in CSS parsing and style updates

- reuse reentrant property-resolution storage during variable substitution
  - skip redundant resolution-slot copies using structural source comparison
  - retain variable cache capacity and resolve variables in place
  - avoid copying canonical names for definition-backed properties
  - propagate resolved inherited properties by reference through widget trees
  - parse animation and transition declarations with allocation-light string views
  - share declaration tokenization between animation and transition parsers
  - avoid empty animation-map and action-query allocations
  - parse border radii without temporary split vectors
  - accept string views when converting UI lengths
  - format color hex values directly without string streams
  - use inline storage for small polygon point collections
  - preserve animation parsing semantics and fix longhand direction handling
  - save eeiv configuration when closing through keyboard shortcuts
  - extend animation, variable-resolution, property-name, and color tests
This commit is contained in:
Martín Lucas Golini
2026-08-09 19:22:00 -03:00
parent 482833d95f
commit 5f419ca58d
20 changed files with 646 additions and 332 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
#ifndef EE_MATHPOLIGON2_H
#define EE_MATHPOLIGON2_H
#include <eepp/core/small_vector.hpp>
#include <eepp/math/line2.hpp>
#include <eepp/math/quad2.hpp>
#include <eepp/math/rect.hpp>
@@ -105,7 +106,7 @@ template <typename T> class Polygon2 {
Uint32 closestPoint( const Vector2<T>& to, T* distance = NULL );
private:
std::vector<Vector2<T>> Vector;
SmallVector<Vector2<T>, 4> Vector;
Vector2<T> Position;
};
@@ -100,6 +100,12 @@ class EE_API AnimationDefinition {
AnimationDirection mDirection = Normal;
AnimationFillMode mFillMode = None;
bool mPaused = false;
static bool isDirectionStringView( std::string_view str );
static AnimationDirection directionFromStringView( std::string_view str );
static bool isAnimationFillModeStringView( std::string_view str );
static AnimationFillMode fillModeFromStringView( std::string_view str );
void setNameView( std::string_view value );
};
inline bool operator==( const AnimationDefinition& a, const AnimationDefinition& b ) {
+13 -2
View File
@@ -11,6 +11,7 @@
#include <eepp/system/time.hpp>
#include <eepp/ui/css/propertyids.hpp>
#include <eepp/ui/css/stylesheetlength.hpp>
#include <span>
#include <string>
using namespace EE::System;
@@ -19,6 +20,7 @@ using namespace EE::Graphics;
namespace EE { namespace UI {
class UINode;
class UIStyle;
}} // namespace EE::UI
namespace EE { namespace UI { namespace CSS {
@@ -29,9 +31,15 @@ class ShorthandDefinition;
struct VariableFunctionCache {
std::string definition;
std::vector<std::string> variableList;
void clear() { variableList.clear(); }
void addVariable( std::string_view variable ) { variableList.emplace_back( variable ); }
};
class EE_API StyleSheetProperty {
friend class EE::UI::UIStyle;
public:
StyleSheetProperty();
@@ -196,7 +204,7 @@ class EE_API StyleSheetProperty {
const String::HashType& getValueHash() const;
const std::vector<VariableFunctionCache>& getVarCache() const;
std::span<const VariableFunctionCache> getVarCache() const;
StyleSheetProperty& setCachedProperty( bool cached );
@@ -205,6 +213,8 @@ class EE_API StyleSheetProperty {
void setImportant( bool important );
protected:
bool hasSameResolutionSource( const StyleSheetProperty& property ) const;
std::string mName;
String::HashType mNameHash{ 0 };
std::string mValue;
@@ -220,6 +230,7 @@ class EE_API StyleSheetProperty {
const ShorthandDefinition* mShorthandDefinition{ nullptr };
std::vector<StyleSheetProperty> mIndexedProperty;
std::vector<VariableFunctionCache> mVarCache;
size_t mVarCacheSize{ 0 };
explicit StyleSheetProperty( bool isVolatile, const PropertyDefinition* definition,
const std::string& value, const Int64& specificity = 0,
@@ -229,7 +240,7 @@ class EE_API StyleSheetProperty {
void checkImportant();
void createIndexed();
void checkVars();
std::vector<VariableFunctionCache> checkVars( const std::string& value );
std::string& mutableValue() { return mValue; }
};
typedef UnorderedMap<Uint32, StyleSheetProperty> StyleSheetProperties;
+16 -1
View File
@@ -1151,6 +1151,13 @@ class EE_API UINode : public Node {
Float convertLengthAsDp( const CSS::StyleSheetLength& length,
const Float& containerLength ) const;
inline Float lengthFromValue( const std::string& value,
const CSS::PropertyRelativeTarget& relativeTarget,
const Float& defaultValue, const Uint32& propertyIndex ) const {
return lengthFromValue( std::string_view{ value }, relativeTarget, defaultValue,
propertyIndex );
}
/**
* @brief Evaluates a CSS length string to a pixel value.
*
@@ -1163,10 +1170,18 @@ class EE_API UINode : public Node {
* @param propertyIndex The property index for multi-value properties (default: 0).
* @return The computed length in pixels.
*/
Float lengthFromValue( const std::string& value,
Float lengthFromValue( std::string_view value,
const CSS::PropertyRelativeTarget& relativeTarget,
const Float& defaultValue = 0, const Uint32& propertyIndex = 0 ) const;
inline Float lengthFromValue( const char* value,
const CSS::PropertyRelativeTarget& relativeTarget,
const Float& defaultValue = 0,
const Uint32& propertyIndex = 0 ) const {
return lengthFromValue( std::string_view{ value }, relativeTarget, defaultValue,
propertyIndex );
}
/**
* @brief Evaluates a CSS property to a pixel value.
*
+36 -5
View File
@@ -10,7 +10,7 @@
#include <eepp/ui/css/stylesheetstyle.hpp>
#include <eepp/ui/css/transitiondefinition.hpp>
#include <eepp/ui/uistate.hpp>
#include <optional>
#include <memory>
namespace EE { namespace Graphics {
class Font;
@@ -96,6 +96,31 @@ class EE_API UIStyle : public UIState {
void applyVarValues( CSS::StyleSheetProperty* style );
protected:
class PropertyResolution {
public:
PropertyResolution( const PropertyResolution& ) = delete;
PropertyResolution& operator=( const PropertyResolution& ) = delete;
PropertyResolution( PropertyResolution&& other ) noexcept;
PropertyResolution& operator=( PropertyResolution&& ) = delete;
~PropertyResolution();
const CSS::StyleSheetProperty* get() const { return mProperty; }
private:
friend class UIStyle;
static constexpr Uint32 NoSlot = static_cast<Uint32>( -1 );
PropertyResolution( UIStyle* owner, const CSS::StyleSheetProperty* property,
Uint32 slot = NoSlot ) :
mOwner( owner ), mProperty( property ), mSlot( slot ) {}
void release();
UIStyle* mOwner;
const CSS::StyleSheetProperty* mProperty;
Uint32 mSlot;
};
UIWidget* mWidget;
std::shared_ptr<CSS::StyleSheetStyle> mElementStyle;
std::shared_ptr<CSS::ElementDefinition> mGlobalDefinition;
@@ -105,8 +130,12 @@ class EE_API UIStyle : public UIState {
UnorderedSet<UIWidget*> mSubscribedWidgets;
UnorderedSet<UIWidget*> mStructurallyVolatileChildren;
Uint32 mStateDepthCounter{ 0 };
Uint32 mPropertyResolutionDepth{ 0 };
Uint64 mLoadedVersion{ 0 };
const CSS::StyleSheet* mLoadedStyleSheet{ nullptr };
/** Lazily allocated for styles that use substitutions. The pointed-to properties remain stable
* during nested resolutions and retain their string/vector capacities between state changes. */
SmallVector<std::unique_ptr<CSS::StyleSheetProperty>, 1> mPropertyResolutionSlots;
bool mChangingState;
bool mForceReapplyProperties;
bool mDisableAnimations;
@@ -118,7 +147,9 @@ class EE_API UIStyle : public UIState {
void applyLightDarkValue( std::string& newValue );
void setVariableFromValue( CSS::StyleSheetProperty* property, const std::string& value );
const CSS::StyleSheetVariable* getVariableRef( const std::string& variable );
void setVariableFromValue( CSS::StyleSheetProperty* property );
void updateState();
@@ -150,9 +181,9 @@ class EE_API UIStyle : public UIState {
CSS::StyleSheetProperty* getLocalProperty( CSS::PropertyId propId );
CSS::StyleSheetProperty*
getResolvedLocalProperty( CSS::PropertyId propId,
std::optional<CSS::StyleSheetProperty>& resolvedProperty );
PropertyResolution resolveProperty( const CSS::StyleSheetProperty* property );
PropertyResolution getResolvedLocalProperty( CSS::PropertyId propId );
void addStructurallyVolatileWidgetFromParent();
+1
View File
@@ -1526,6 +1526,7 @@ class EE_API UIWidget : public UINode {
void calculateAutoMargin();
void rebuildClassHashes();
void propagateInheritedPropertyResolved( const CSS::StyleSheetProperty& property );
/**
* @brief Default constructor.