feat: Implement texture caching system with dirty flag optimization (closes #144)

- Add cache_subtree property on Frame for opt-in RenderTexture caching
- Add PyTexture::from_rendered() factory for runtime texture creation
- Add snapshot= parameter to Sprite for creating sprites from Frame content
- Implement content_dirty vs composite_dirty distinction:
  - markContentDirty(): content changed, invalidate self and ancestors
  - markCompositeDirty(): position changed, ancestors need recomposite only
- Update all UIDrawable position setters to use markCompositeDirty()
- Add quick exit workaround for cleanup segfaults

Benchmark: deep_nesting_cached is 3.7x faster (0.09ms vs 0.35ms)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
John McCardle 2025-11-28 19:30:24 -05:00
commit 68f8349fe8
13 changed files with 220 additions and 56 deletions

View file

@ -15,9 +15,16 @@ private:
sf::Texture texture;
std::string source;
int sheet_width, sheet_height;
// Private default constructor for factory methods
PyTexture() : source("<uninitialized>"), sprite_width(0), sprite_height(0), sheet_width(0), sheet_height(0) {}
public:
int sprite_width, sprite_height; // just use them read only, OK?
PyTexture(std::string filename, int sprite_w, int sprite_h);
// #144: Factory method to create texture from rendered content (snapshot)
static std::shared_ptr<PyTexture> from_rendered(sf::RenderTexture& render_tex);
sf::Sprite sprite(int index, sf::Vector2f pos = sf::Vector2f(0, 0), sf::Vector2f s = sf::Vector2f(1.0, 1.0));
int getSpriteCount() const { return sheet_width * sheet_height; }