Skip to content

Commit 941d0e0

Browse files
authored
Merge pull request #53 from philstopford/copilot/improve-viewport-update-time
Fix async void pattern causing viewport update delays on Linux/GTK + Add comprehensive diagnostics
2 parents 94d2503 + 1a5655a commit 941d0e0

File tree

6 files changed

+506
-7
lines changed

6 files changed

+506
-7
lines changed

Eto/Eto.Veldrid.Gtk/GtkVeldridSurfaceHandler.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ private void glArea_Resize(object o, ResizeArgs args)
9696

9797
private void glArea_Render(object o, RenderArgs args)
9898
{
99+
var enableDiag = Environment.GetEnvironmentVariable("VIEWPORT_DIAGNOSTICS") == "1";
100+
if (enableDiag)
101+
{
102+
Console.WriteLine($"[GTK DIAG] glArea_Render called, skipDraw={skipDraw}");
103+
}
104+
99105
if (!skipDraw)
100106
{
101107
skipDraw = true;
@@ -114,7 +120,19 @@ private void glArea_Render(object o, RenderArgs args)
114120

115121
// It's important to only issue Veldrid commands in OnDraw,
116122
// since we only have a GL context current in the worker here.
123+
if (enableDiag)
124+
{
125+
Console.WriteLine($"[GTK DIAG] Calling OnDraw callback");
126+
}
127+
128+
var drawTimer = System.Diagnostics.Stopwatch.StartNew();
117129
Callback.OnDraw(Widget, EventArgs.Empty);
130+
drawTimer.Stop();
131+
132+
if (enableDiag)
133+
{
134+
Console.WriteLine($"[GTK DIAG] OnDraw callback completed in {drawTimer.ElapsedMilliseconds}ms");
135+
}
118136

119137
// Clear the context from the worker so GTK can use it again.
120138
// This action needs to wait so the context is cleared before we continue
@@ -126,6 +144,11 @@ private void glArea_Render(object o, RenderArgs args)
126144
glArea?.MakeCurrent();
127145
}
128146
skipDraw = false;
147+
148+
if (enableDiag)
149+
{
150+
Console.WriteLine($"[GTK DIAG] glArea_Render completed");
151+
}
129152
}
130153

131154
private void MakeCurrent()
@@ -177,12 +200,22 @@ void VeldridSurface.IOpenGL.ResizeSwapchain(uint width, uint height)
177200

178201
void Forms.Control.IHandler.Invalidate(Rectangle rect, bool invalidateChildren)
179202
{
203+
var enableDiag = Environment.GetEnvironmentVariable("VIEWPORT_DIAGNOSTICS") == "1";
204+
if (enableDiag)
205+
{
206+
Console.WriteLine($"[GTK DIAG] Invalidate(rect) called, skipDraw={skipDraw}");
207+
}
180208
skipDraw = false;
181209
glArea?.QueueRender();
182210
}
183211

184212
void Forms.Control.IHandler.Invalidate(bool invalidateChildren)
185213
{
214+
var enableDiag = Environment.GetEnvironmentVariable("VIEWPORT_DIAGNOSTICS") == "1";
215+
if (enableDiag)
216+
{
217+
Console.WriteLine($"[GTK DIAG] Invalidate() called, skipDraw={skipDraw}");
218+
}
186219
skipDraw = false;
187220
glArea?.QueueRender();
188221
}

Eto/Eto.VeldridSurface/VeldridDriver_Draw.cs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,80 @@
11
using Eto.Drawing;
22
using System.Numerics;
33
using Veldrid;
4+
using System.Diagnostics;
45

56
namespace VeldridEto;
67

78
public partial class VeldridDriver
89
{
910
private bool drawing = false;
1011
private bool done_drawing = false;
11-
private async void pUpdateViewport()
12+
private async Task pUpdateViewportAsync()
1213
{
1314
if ((!ovpSettings.changed) || (Surface!.GraphicsDevice == null) ||
1415
(!Surface.Visible) || (Surface.Width <= 0) || (Surface.Height <= 0) || drawing)
1516
{
17+
if (EnableDiagnostics)
18+
{
19+
Console.WriteLine($"[VIEWPORT DIAG] pUpdateViewportAsync early return: changed={ovpSettings.changed}, GraphicsDevice={(Surface?.GraphicsDevice != null)}, Visible={Surface?.Visible}, Width={Surface?.Width}, Height={Surface?.Height}, drawing={drawing}");
20+
}
1621
return;
1722
}
1823

1924
drawing = true;
2025
done_drawing = false;
2126

27+
if (EnableDiagnostics)
28+
{
29+
Console.WriteLine($"[VIEWPORT DIAG] Starting geometry processing...");
30+
Console.WriteLine($"[VIEWPORT DIAG] Polygons: fg={ovpSettings.polyList?.Count ?? 0}, bg={ovpSettings.bgPolyList?.Count ?? 0}, tess={ovpSettings.tessPolyList?.Count ?? 0}");
31+
Console.WriteLine($"[VIEWPORT DIAG] Lines: {ovpSettings.lineList?.Count ?? 0}");
32+
}
33+
34+
var taskTimer = Stopwatch.StartNew();
35+
2236
// Trying to push things into tasks to speed up the computation. Not sure if this is entirely robust.
23-
await Task.WhenAll(drawAxes(), drawGrid(), drawLines(), drawPolygons());
37+
if (EnableDiagnostics)
38+
{
39+
// With diagnostics, time each task individually
40+
var axesTimer = Stopwatch.StartNew();
41+
var axesTask = drawAxes();
42+
43+
var gridTimer = Stopwatch.StartNew();
44+
var gridTask = drawGrid();
45+
46+
var linesTimer = Stopwatch.StartNew();
47+
var linesTask = drawLines();
48+
49+
var polygonsTimer = Stopwatch.StartNew();
50+
var polygonsTask = drawPolygons();
51+
52+
// Wait for all tasks to complete
53+
await Task.WhenAll(axesTask, gridTask, linesTask, polygonsTask);
54+
55+
// Stop timers and report (all tasks are complete at this point)
56+
axesTimer.Stop();
57+
gridTimer.Stop();
58+
linesTimer.Stop();
59+
polygonsTimer.Stop();
60+
61+
Console.WriteLine($"[VIEWPORT DIAG] drawAxes() took {axesTimer.ElapsedMilliseconds}ms");
62+
Console.WriteLine($"[VIEWPORT DIAG] drawGrid() took {gridTimer.ElapsedMilliseconds}ms");
63+
Console.WriteLine($"[VIEWPORT DIAG] drawLines() took {linesTimer.ElapsedMilliseconds}ms");
64+
Console.WriteLine($"[VIEWPORT DIAG] drawPolygons() took {polygonsTimer.ElapsedMilliseconds}ms");
65+
}
66+
else
67+
{
68+
// Without diagnostics, run tasks normally
69+
await Task.WhenAll(drawAxes(), drawGrid(), drawLines(), drawPolygons());
70+
}
71+
72+
taskTimer.Stop();
73+
74+
if (EnableDiagnostics)
75+
{
76+
Console.WriteLine($"[VIEWPORT DIAG] All drawing tasks completed in {taskTimer.ElapsedMilliseconds}ms");
77+
}
2478

2579
done_drawing = true;
2680
}
@@ -115,16 +169,15 @@ private Task drawPolygons()
115169
{
116170
float alpha = ovpSettings.tessPolyList[poly].alpha;
117171
float polyZ = poly * polyZStep;
118-
Parallel.For(0, 3, pt =>
119-
// for(int pt = 0; pt < 3; pt++)
172+
for (int pt = 0; pt < 3; pt++)
120173
{
121174
tessPolyList[(poly * 3) + pt] = new VertexPositionColor(
122175
new Vector3(ovpSettings.tessPolyList[poly].poly[pt].X,
123176
ovpSettings.tessPolyList[poly].poly[pt].Y, polyZ),
124177
new RgbaFloat(ovpSettings.tessPolyList[poly].color.R,
125178
ovpSettings.tessPolyList[poly].color.G, ovpSettings.tessPolyList[poly].color.B,
126179
alpha));
127-
});
180+
}
128181
});
129182

130183
tessIndices = new uint[tessPolyListCount * 3];

Eto/Eto.VeldridSurface/VeldridDriver_Handlers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ private void setFocus(object sender, EventArgs e)
211211

212212
private void Clock_Elapsed(object sender, EventArgs e)
213213
{
214-
pUpdateViewport();
214+
pUpdateViewportAsync().GetAwaiter().GetResult();
215215
if (done_drawing)
216216
{
217217
updateHostFunc?.Invoke();

Eto/Eto.VeldridSurface/VeldridDriver_Public.cs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
using Eto;
12
using Eto.Drawing;
23
using Eto.Forms;
34
using VeldridEto;
5+
using System.Diagnostics;
46

57
namespace VeldridEto;
68

@@ -24,15 +26,65 @@ public partial class VeldridDriver
2426
// Is saved location valid?
2527
public bool savedLocation_valid { get; private set; }
2628

29+
// Diagnostic flag to enable/disable performance logging
30+
private bool _enableDiagnostics = false;
31+
public bool EnableDiagnostics
32+
{
33+
get => _enableDiagnostics || Environment.GetEnvironmentVariable("VIEWPORT_DIAGNOSTICS") == "1";
34+
set => _enableDiagnostics = value;
35+
}
36+
2737
public void updateViewport()
2838
{
29-
pUpdateViewport();
39+
var totalTimer = Stopwatch.StartNew();
40+
41+
if (EnableDiagnostics)
42+
{
43+
Console.WriteLine($"[VIEWPORT DIAG] updateViewport() called on {(Platform.Instance.IsGtk ? "GTK" : Platform.Instance.IsWpf ? "WPF" : "Other")} platform");
44+
Console.WriteLine($"[VIEWPORT DIAG] ovpSettings.changed={ovpSettings.changed}, drawing={drawing}, done_drawing={done_drawing}");
45+
}
46+
47+
var asyncTimer = Stopwatch.StartNew();
48+
pUpdateViewportAsync().GetAwaiter().GetResult();
49+
asyncTimer.Stop();
50+
51+
if (EnableDiagnostics)
52+
{
53+
Console.WriteLine($"[VIEWPORT DIAG] pUpdateViewportAsync completed in {asyncTimer.ElapsedMilliseconds}ms");
54+
Console.WriteLine($"[VIEWPORT DIAG] After async: done_drawing={done_drawing}");
55+
}
56+
3057
if (done_drawing)
3158
{
59+
var callbackTimer = Stopwatch.StartNew();
3260
updateHostFunc?.Invoke();
61+
callbackTimer.Stop();
62+
63+
if (EnableDiagnostics && callbackTimer.ElapsedMilliseconds > 0)
64+
{
65+
Console.WriteLine($"[VIEWPORT DIAG] updateHostFunc callback took {callbackTimer.ElapsedMilliseconds}ms");
66+
}
67+
68+
var invalidateTimer = Stopwatch.StartNew();
3369
Surface!.Invalidate();
70+
invalidateTimer.Stop();
71+
72+
if (EnableDiagnostics)
73+
{
74+
Console.WriteLine($"[VIEWPORT DIAG] Surface.Invalidate() took {invalidateTimer.ElapsedMilliseconds}ms");
75+
}
76+
3477
ovpSettings.changed = false;
3578
drawing = false;
79+
done_drawing = false;
80+
}
81+
82+
totalTimer.Stop();
83+
84+
if (EnableDiagnostics)
85+
{
86+
Console.WriteLine($"[VIEWPORT DIAG] Total updateViewport() time: {totalTimer.ElapsedMilliseconds}ms");
87+
Console.WriteLine($"[VIEWPORT DIAG] ----------------------------------------");
3688
}
3789
}
3890

0 commit comments

Comments
 (0)