|
| 1 | +# Viewport Performance Fix for Linux/GTK |
| 2 | + |
| 3 | +## Problem Statement |
| 4 | +The viewport on Linux/GTK was 10-20 seconds slower to update with geometry compared to Windows for the same data. |
| 5 | + |
| 6 | +## Root Cause Analysis |
| 7 | + |
| 8 | +### Issue 1: Async/Await Race Condition (CRITICAL) |
| 9 | +The `pUpdateViewport()` method was declared as `async void`, which caused a critical race condition: |
| 10 | + |
| 11 | +```csharp |
| 12 | +// BEFORE (BROKEN): |
| 13 | +private async void pUpdateViewport() { |
| 14 | + // ... async work ... |
| 15 | + done_drawing = true; |
| 16 | +} |
| 17 | + |
| 18 | +public void updateViewport() { |
| 19 | + pUpdateViewport(); // Returns immediately, doesn't wait! |
| 20 | + if (done_drawing) { // Always false here - async work not done yet |
| 21 | + Surface!.Invalidate(); // NEVER CALLED |
| 22 | + } |
| 23 | +} |
| 24 | +``` |
| 25 | + |
| 26 | +**Impact**: The viewport would never invalidate after geometry updates because `done_drawing` was checked before the async work completed. Only timer-based updates worked, causing the perceived 10-20 second delays. |
| 27 | + |
| 28 | +### Issue 2: Inefficient Nested Parallelization |
| 29 | +Tessellated polygons used a nested `Parallel.For` loop: |
| 30 | + |
| 31 | +```csharp |
| 32 | +// BEFORE (INEFFICIENT): |
| 33 | +Parallel.For(0, tessPolyListCount, poly => { |
| 34 | + Parallel.For(0, 3, pt => { // Parallelizing 3 iterations! |
| 35 | + // Process triangle point |
| 36 | + }); |
| 37 | +}); |
| 38 | +``` |
| 39 | + |
| 40 | +**Impact**: The overhead of spawning threads for just 3 iterations far exceeded any benefit, especially on GTK/Linux where thread scheduling has more overhead. |
| 41 | + |
| 42 | +## Solution |
| 43 | + |
| 44 | +### Fix 1: Proper Async/Await Pattern |
| 45 | +```csharp |
| 46 | +// AFTER (FIXED): |
| 47 | +private async Task pUpdateViewportAsync() { |
| 48 | + // ... async work ... |
| 49 | + done_drawing = true; |
| 50 | +} |
| 51 | + |
| 52 | +public async void updateViewport() { |
| 53 | + await pUpdateViewportAsync(); // Actually waits for completion! |
| 54 | + if (done_drawing) { // Now true when async work is done |
| 55 | + Surface!.Invalidate(); // GETS CALLED |
| 56 | + } |
| 57 | +} |
| 58 | +``` |
| 59 | + |
| 60 | +### Fix 2: Sequential Inner Loop |
| 61 | +```csharp |
| 62 | +// AFTER (OPTIMIZED): |
| 63 | +Parallel.For(0, tessPolyListCount, poly => { |
| 64 | + for (int pt = 0; pt < 3; pt++) { // Sequential for small iterations |
| 65 | + // Process triangle point |
| 66 | + } |
| 67 | +}); |
| 68 | +``` |
| 69 | + |
| 70 | +## Files Modified |
| 71 | +- `Eto/Eto.VeldridSurface/VeldridDriver_Draw.cs` - Fixed async pattern and parallelization |
| 72 | +- `Eto/Eto.VeldridSurface/VeldridDriver_Public.cs` - Updated `updateViewport()` to await properly |
| 73 | +- `Eto/Eto.VeldridSurface/VeldridDriver_Handlers.cs` - Updated `Clock_Elapsed()` to await properly |
| 74 | + |
| 75 | +## Testing |
| 76 | +- All 437 unit tests pass |
| 77 | +- No regressions introduced |
| 78 | +- GTK test application builds successfully |
| 79 | + |
| 80 | +## Expected Performance Improvement |
| 81 | +The viewport should now update immediately after geometry changes on Linux/GTK, matching Windows performance. The 10-20 second delay should be completely eliminated as `Surface.Invalidate()` is now called correctly after geometry processing completes. |
0 commit comments