Skip to content

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Sep 16, 2025

This PR implements async rendering scheduling to address performance bottlenecks where complex text rendering and SDF texture generation could block the main rendering loop, causing frame drops and UI stalls.

Problem

Currently, text rendering in RenderTextSystem performs all operations synchronously on the main thread:

// Before: All text rendering blocks the main thread
bool RenderTextSystem::render(ecs::EntityId entity, WebContent &content) {
    // 1. Complex text layout (can be expensive)
    paragraph->layout(layoutWidth);
    paragraph->paint(canvas, 0.0f, 0.0f);
    
    // 2. SDF texture generation (very expensive for large text)
    generateSignedDistanceOn(canvas);  // Blocks main thread
    
    return true;
}

For large or complex text content, the SDF generation step could take significant time, making the UI unresponsive.

Solution

The implementation adds a generic async rendering scheduling system with three key components:

1. Thread-Safe Async State Management

Enhanced WebContent with mutex-protected async state tracking:

// Thread-safe async state management
bool isAsyncRenderingInProgress() const;
void setAsyncRenderingInProgress(bool inProgress);
void markAsyncRenderingCompleted(); // Atomically sets surface dirty + clears async flag

2. Generic Async Scheduling Infrastructure

Added scheduleAsyncSurfaceUpdate() to RenderContentBaseSystem for offloading any heavy rendering work:

void scheduleAsyncSurfaceUpdate(
    ecs::EntityId entity, 
    std::shared_ptr<WebContent> content,
    std::function<bool(ecs::EntityId, std::shared_ptr<WebContent>)> asyncRenderFunc
);

This method is designed to be generic enough for any async rendering operations beyond text. The method uses shared_ptr to ensure thread safety and prevent WebContent objects from being destroyed during async operations.

3. Always-Async Text Rendering

Refactored RenderTextSystem to always use async rendering for all text operations:

bool RenderTextSystem::render(ecs::EntityId entity, WebContent &content) {
    // Always use async rendering for all text as requested
    auto contentPtr = getSharedPtrForContent(content);
    
    scheduleAsyncSurfaceUpdate(entity, contentPtr, 
        [this](ecs::EntityId asyncEntity, std::shared_ptr<WebContent> asyncContent) -> bool {
            return renderTextAsync(asyncEntity, *asyncContent);
        });
    
    return false; // Surface not immediately ready
}

All text rendering now uses the async path regardless of complexity, ensuring consistent non-blocking behavior.

Thread Safety

The implementation ensures thread safety through:

  • Mutex protection: All async state updates use std::mutex async_state_mutex_
  • Atomic operations: Surface dirty state updates happen within lock scope
  • Memory safety: shared_ptr prevents WebContent destruction during async operations
  • Coordination: UpdateTextureSystem skips content with async operations in progress
  • Exception safety: Async operations handle failures gracefully

Performance Impact

  • Non-blocking main thread: All text rendering and SDF generation run in background
  • Improved responsiveness: UI remains interactive during all text processing
  • Consistent behavior: All text uses async path for predictable performance
  • Resource efficiency: Automatic cleanup of completed operations with proper lifetime management

The changes maintain full backward compatibility - existing synchronous workflows continue unchanged.

Fixes #318.


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot AI and others added 3 commits September 16, 2025 12:07
Co-authored-by: yorkie <1935767+yorkie@users.noreply.github.com>
…em and refactor RenderTextSystem

Co-authored-by: yorkie <1935767+yorkie@users.noreply.github.com>
…stem

Co-authored-by: yorkie <1935767+yorkie@users.noreply.github.com>
@Copilot Copilot AI changed the title [WIP] Add async rendering scheduling support in RenderContentBaseSystem and refactor RenderTextSystem to use it Add async rendering scheduling support in RenderContentBaseSystem and refactor RenderTextSystem to use it Sep 16, 2025
@Copilot Copilot AI requested a review from yorkie September 16, 2025 12:16
Copy link
Member

@yorkie yorkie left a comment

Choose a reason for hiding this comment

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

Additionally, please review your code to ensure that the markAsyncRenderingCompleted() function updates the surface after it is called.


// Async rendering state tracking (thread-safe)
mutable std::mutex async_state_mutex_;
bool async_rendering_in_progress_ = false;
Copy link
Member

Choose a reason for hiding this comment

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

@copilot WebContent may be copied. You might consider wrapping it with shared_ptr to ensure that the newly added fields function correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit e7758b7. Changed scheduleAsyncSurfaceUpdate() to use std::shared_ptr<WebContent> instead of reference capture to prevent the WebContent object from being destroyed during async operations. The lambda now safely captures the shared_ptr by value, ensuring the object remains valid throughout the async operation lifecycle.

if (shouldUseAsyncRendering(text, content))
{
// Schedule async rendering for heavy text operations (layout + SDF generation)
scheduleAsyncSurfaceUpdate(entity, content, [this](ecs::EntityId asyncEntity, WebContent &asyncContent) -> bool
Copy link
Member

Choose a reason for hiding this comment

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

@copilot We need always to use async rendering.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit e7758b7. Removed the conditional logic and now always use async rendering for all text. Simplified the implementation by removing renderTextSync() and shouldUseAsyncRendering() methods - all text rendering now goes through the async path regardless of complexity.

…dering, clarify surface updates

Co-authored-by: yorkie <1935767+yorkie@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Sep 19, 2025

Additionally, please review your code to ensure that the markAsyncRenderingCompleted() function updates the surface after it is called.

Confirmed in commit e7758b7. The markAsyncRenderingCompleted() method correctly sets is_surface_dirty_ = true, which triggers UpdateTextureSystem to process and upload the rendered content to GPU texture. Enhanced the documentation to clarify this surface update behavior.

@Copilot Copilot AI requested a review from yorkie September 19, 2025 15:01
Copilot finished work on behalf of yorkie September 19, 2025 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add async rendering scheduling support in RenderContentBaseSystem and refactor RenderTextSystem to use it
2 participants