20 private readonly SKGLControl _glControl;
21 private readonly Control _presentationControl;
22 private readonly EventHandler _resizeHandler;
27 private Action? _afterFrameRenderHandler;
31 private bool? _appliedVSync;
35 private bool _grContextReady;
38 private int _pendingResize;
43 private int _pendingInvalidate;
48 public SKColor
ClearColor {
get;
set; } = SKColors.Black;
81 if (_presentationControl.IsDisposed || !_presentationControl.IsHandleCreated)
return;
83 var sz = _presentationControl.ClientSize;
86 if (sz.Width > 0 && sz.Height > 0)
87 Interlocked.Exchange(ref _pendingResize, 1);
89 Interlocked.Exchange(ref _pendingResize, 0);
109 Math.Max(1, presentationControl.ClientSize.
Width),
110 Math.Max(1, presentationControl.ClientSize.
Height),
111 presentationControl.ClientSize.
Width > 0 && presentationControl.ClientSize.
Height > 0)
113 _glControl = gl ??
throw new ArgumentNullException(nameof(gl));
114 _presentationControl = presentationControl ??
throw new ArgumentNullException(nameof(presentationControl));
122 _glControl.PaintSurface += OnPaintSurface;
123 _presentationControl.Resize += _resizeHandler;
143 _host = host ??
throw new ArgumentNullException(nameof(host));
144 _host.Telemetry?.Define(
"presentation.cpu.ms");
145 _host.Telemetry?.Define(
"render.snapshot.cpu.ms");
146 _host.Telemetry?.Define(
"blit.cpu.ms");
147 _host.Telemetry?.Define(
"flush.cpu.ms");
156 _afterFrameRenderHandler = QueueInvalidate;
157 Engine.Instance.AfterFrameRender += _afterFrameRenderHandler;
167 public override void Present(SKImage bufferImage, SKRectI bufferRect, SKRect destRect)
169 bufferImage.Dispose();
172 private void QueueInvalidate()
174 if (_glControl.IsDisposed || _presentationControl.IsDisposed)
return;
175 if (Interlocked.CompareExchange(ref _pendingInvalidate, 1, 0) != 0)
return;
181 if (!_glControl.IsDisposed
182 && !_presentationControl.IsDisposed
183 && _presentationControl.ClientSize.Width > 0
184 && _presentationControl.ClientSize.Height > 0)
186 _glControl.Invalidate();
193 Interlocked.Exchange(ref _pendingInvalidate, 0);
198 private void OnPaintSurface(
object? sender, SKPaintGLSurfaceEventArgs e)
200 long telemetryGeneration = _host?.Telemetry?.BeginSample() ?? 0;
202 long callbackStartTick = collectDiagnostics ? HighResTimer.GetCurrentTick() : 0;
203 long renderAndSnapshotTicks = 0;
208 if (_gpuBackbuffer !=
null)
210 bool desiredVSync = _gpuBackbuffer.VSync;
211 if (_appliedVSync != desiredVSync)
213 _glControl.VSync = desiredVSync;
214 _appliedVSync = desiredVSync;
223 if (!_grContextReady)
227 _grContextReady =
true;
228 Interlocked.Exchange(ref _pendingResize, 0);
231 else if (Interlocked.Exchange(ref _pendingResize, 0) == 1)
239 if (width > 0 && height > 0)
245 _gpuBackbuffer?.EnsureInitialized(
GrContext);
247 var canvas = e.Surface.Canvas;
263 long renderStartTick = collectDiagnostics ? HighResTimer.GetCurrentTick() : 0;
264 using var img = _host.GlRenderAndSnapshot();
265 long renderEndTick = collectDiagnostics ? HighResTimer.GetCurrentTick() : 0;
267 if (collectDiagnostics)
268 renderAndSnapshotTicks = renderEndTick - renderStartTick;
272 long blitStartTick = collectDiagnostics ? HighResTimer.GetCurrentTick() : 0;
274 long blitEndTick = collectDiagnostics ? HighResTimer.GetCurrentTick() : 0;
276 if (collectDiagnostics)
277 blitTicks = blitEndTick - blitStartTick;
290 long flushStartTick = collectDiagnostics ? HighResTimer.GetCurrentTick() : 0;
291 _glControl.GRContext?.Flush();
292 long flushEndTick = collectDiagnostics ? HighResTimer.GetCurrentTick() : 0;
294 if (collectDiagnostics)
295 flushTicks = flushEndTick - flushStartTick;
298 _gpuBackbuffer?.RecordFrame();
300 if (collectDiagnostics)
302 long callbackEndTick = HighResTimer.GetCurrentTick();
303 if (telemetryGeneration != 0)
305 var source = _host!.Telemetry!;
306 source.Record(telemetryGeneration,
"presentation.count", 1);
307 source.Record(telemetryGeneration,
"presentation.cpu.ms", HighResTimer.GetDuration(callbackStartTick, callbackEndTick) * 1000d);
308 source.Record(telemetryGeneration,
"render.snapshot.cpu.ms", HighResTimer.GetDuration(0, renderAndSnapshotTicks) * 1000d);
309 source.Record(telemetryGeneration,
"blit.cpu.ms", HighResTimer.GetDuration(0, blitTicks) * 1000d);
310 source.Record(telemetryGeneration,
"flush.cpu.ms", HighResTimer.GetDuration(0, flushTicks) * 1000d);
313 HighResTimer.GetDuration(callbackStartTick, callbackEndTick) * 1000d,
314 HighResTimer.GetDuration(0, renderAndSnapshotTicks) * 1000d,
315 HighResTimer.GetDuration(0, blitTicks) * 1000d,
316 HighResTimer.GetDuration(0, flushTicks) * 1000d));
325 if (_afterFrameRenderHandler !=
null)
327 Engine.Instance.AfterFrameRender -= _afterFrameRenderHandler;
328 _afterFrameRenderHandler =
null;
331 if (!_glControl.IsDisposed)
332 _glControl.PaintSurface -= OnPaintSurface;
334 if (!_presentationControl.IsDisposed)
335 _presentationControl.Resize -= _resizeHandler;