This guide explains how to use the comprehensive diagnostic logging added to help troubleshoot viewport update delays on Linux/GTK.
The viewport rendering system now includes detailed timing and diagnostic information at multiple levels:
- Overall viewport update timing
- Individual drawing task performance (axes, grid, lines, polygons)
- GTK-specific rendering pipeline events
- Platform detection and state information
There are two ways to enable diagnostic output:
Set the VIEWPORT_DIAGNOSTICS environment variable before running your application:
# On Linux/GTK
export VIEWPORT_DIAGNOSTICS=1
./YourApplication
# Or run directly
VIEWPORT_DIAGNOSTICS=1 ./YourApplication# On Windows
$env:VIEWPORT_DIAGNOSTICS="1"
.\YourApplication.exeThis method automatically enables diagnostics for both the VeldridDriver and GTK-specific handlers.
For applications that embed the viewport, you can enable diagnostics programmatically:
// After creating the VeldridDriver
driver.EnableDiagnostics = true;This method only affects the VeldridDriver diagnostics, not GTK-specific output. Use environment variable for complete diagnostics.
When diagnostics are enabled, you'll see output like this:
[VIEWPORT DIAG] updateViewport() called on GTK platform
[VIEWPORT DIAG] ovpSettings.changed=True, drawing=False, done_drawing=False
[VIEWPORT DIAG] Starting geometry processing...
[VIEWPORT DIAG] Polygons: fg=100, bg=50, tess=200
[VIEWPORT DIAG] Lines: 75
[VIEWPORT DIAG] drawAxes() took 2ms
[VIEWPORT DIAG] drawGrid() took 5ms
[VIEWPORT DIAG] drawLines() took 12ms
[VIEWPORT DIAG] drawPolygons() took 45ms
[VIEWPORT DIAG] All drawing tasks completed in 47ms
[VIEWPORT DIAG] pUpdateViewportAsync completed in 48ms
[VIEWPORT DIAG] After async: done_drawing=True
[VIEWPORT DIAG] updateHostFunc callback took 1ms
[VIEWPORT DIAG] Surface.Invalidate() took 0ms
[VIEWPORT DIAG] Total updateViewport() time: 50ms
[VIEWPORT DIAG] ----------------------------------------
On GTK, you'll also see platform-specific rendering events:
[GTK DIAG] Invalidate() called, skipDraw=False
[GTK DIAG] glArea_Render called, skipDraw=False
[GTK DIAG] Calling OnDraw callback
[GTK DIAG] OnDraw callback completed in 25ms
[GTK DIAG] glArea_Render completed
- Normal: < 100ms for typical geometry
- Concerning: > 500ms
- Critical: > 1000ms (1 second)
Look for: Total updateViewport() time: XXms
Individual task times help identify bottlenecks:
drawPolygons()- Usually the longest (polygon tessellation and vertex buffer creation)drawLines()- Medium durationdrawGrid()anddrawAxes()- Usually fastest
- Check time between
Invalidate()andglArea_Render - Large gaps (> 100ms) indicate GTK event loop delays
- Monitor
OnDraw callback completedtime
- Similar but without GTK-specific messages
- Generally faster due to better thread affinity
Watch for warning signs:
ovpSettings.changed=Falsewhen it should be Truedrawing=Truewhen calling updateViewport (indicates concurrent calls)done_drawing=Falseafter async completion (shouldn't happen)- Early returns from pUpdateViewportAsync (check the reason)
Symptom: drawPolygons() took XXXms where XXX > 100
Possible Causes:
- Too many polygons
- Complex tessellation
- Inefficient parallel processing
Debug: Check polygon counts in output:
[VIEWPORT DIAG] Polygons: fg=10000, bg=5000, tess=20000
Symptom: Long gap between Invalidate() and glArea_Render
Possible Causes:
- GTK main thread busy with other tasks
- Heavy UI updates blocking event processing
- Thread affinity issues
Debug: Look for patterns in timing between these events
Symptom: updateHostFunc callback took XXXms where XXX > 50
Possible Causes:
- Client application doing heavy work in callback
- Blocking operations in update handler
- Recursive viewport updates
Debug: Review what the client's updateHostFunc does
When comparing performance between platforms, look for:
-
Total time differences:
- Windows: Typically 20-50ms
- Linux/GTK: Should be similar (30-60ms)
- Differences > 100ms indicate platform-specific issues
-
Event loop responsiveness:
- Windows: Immediate invalidation → render
- GTK: May have slight delay (10-30ms normal)
- Delays > 100ms on GTK indicate event loop issues
-
Drawing task performance:
- Should be similar across platforms
- Significant differences indicate thread/parallelization issues
When reporting performance problems, include:
- Full diagnostic output from both platforms if comparing
- Geometry complexity: Number of polygons, lines, points
- Platform information:
- OS version
- GTK version (on Linux)
- .NET version
- Timing patterns:
- Average times
- Worst-case times
- Any patterns (e.g., first update slow, subsequent fast)
- Enable diagnostics:
export VIEWPORT_DIAGNOSTICS=1
./quilt-
Perform the slow operation and capture output
-
Look for:
- Which phase is slow? (geometry processing vs rendering vs invalidation)
- Are there GTK event loop delays?
- How does it compare to Windows?
-
Common fixes:
- If polygon processing is slow: Reduce geometry complexity or optimize tessellation
- If GTK event delays: Check for UI thread blocking
- If invalidation is slow: May be driver/compositor issue
To disable diagnostics:
# Remove or unset the environment variable
unset VIEWPORT_DIAGNOSTICS
# Or set to 0
export VIEWPORT_DIAGNOSTICS=0Or programmatically:
driver.EnableDiagnostics = false;- Reduce geometry updates: Only call
updateViewport()when geometry actually changes - Batch updates: Group multiple geometry changes before updating
- Monitor callback performance: Keep
updateHostFunclightweight - Check platform-specific code: GTK may need different optimization strategies than WPF
- Diagnostics use
System.Diagnostics.Stopwatchfor high-precision timing - Output goes to
Console.WriteLine(stderr by default) - Minimal performance impact when disabled
- Per-task timing: All timers start when tasks are created, stop after
Task.WhenAllcompletes- Provides accurate wall-clock time for each task
- Tasks run in parallel, so individual times may overlap
- Total time shows actual elapsed time for all tasks
- Environment variable checked on each access (can enable/disable without restart)
- No threading issues or deadlocks - all diagnostic output happens after tasks complete