18 internal RenderFrameMailbox FrameMailbox {
get; } =
new();
20 internal virtual void ProduceRenderFrameSnapshot(
long tick) { }
33 RenderSurfaceHostRegistry.Register(
this);
50 internal virtual void RequestRenderScale(
float scale) { }
51 internal virtual void InvalidatePresentation() { }
119 if (UsesRenderFrameSnapshots)
120 return ReplayRenderFrameSnapshot();
122 bool collectSynchronizationDiagnostics =
124 long waitStarted = collectSynchronizationDiagnostics
125 ? HighResTimer.GetCurrentTick()
127 long lockAcquired = 0;
128 long lockReleased = 0;
131 RenderStateSynchronization.EnterGpuRender();
134 if (collectSynchronizationDiagnostics)
135 lockAcquired = HighResTimer.GetCurrentTick();
137 var tick = HighResTimer.GetCurrentTick();
139 RenderToBackbuffer(tick);
146 if (collectSynchronizationDiagnostics)
147 lockReleased = HighResTimer.GetCurrentTick();
151 RenderStateSynchronization.ExitGpuRender();
154 if (collectSynchronizationDiagnostics)
158 HighResTimer.GetDuration(waitStarted, lockAcquired) * 1000d,
159 HighResTimer.GetDuration(lockAcquired, lockReleased) * 1000d));
165 private SKImage? ReplayRenderFrameSnapshot()
168 long telemetryGeneration =
Telemetry?.BeginSample() ?? 0;
169 bool collectDiagnostics = diagnostics is not
null || telemetryGeneration != 0;
170 var slot = FrameMailbox.TryAcquire();
171 if (slot is
null)
return Backbuffer.Snapshot();
173 long acquired = !collectDiagnostics ? 0 : HighResTimer.GetCurrentTick();
174 var counters = !collectDiagnostics ? default : FrameMailbox.Counters;
175 long replayEnd = acquired;
176 bool replayCompleted =
false;
177 long pictureReplayTicks = 0;
178 long backbufferFlushTicks = 0;
179 long snapshotTicks = 0;
180 int commands = !collectDiagnostics ? 0 : slot.Frame!.CommandCount;
181 double age = !collectDiagnostics ? 0 :
182 HighResTimer.GetDuration(slot.Frame!.ProducedTick, acquired) * 1000d;
186 var frame = slot.Frame!;
192 long snapshotStart = !collectDiagnostics ? 0 : HighResTimer.GetCurrentTick();
194 if (collectDiagnostics)
195 snapshotTicks = HighResTimer.GetCurrentTick() - snapshotStart;
196 return resizedSnapshot;
201 long pictureStart = !collectDiagnostics ? 0 : HighResTimer.GetCurrentTick();
203 long pictureEnd = !collectDiagnostics ? 0 : HighResTimer.GetCurrentTick();
206 long flushEnd = !collectDiagnostics ? 0 : HighResTimer.GetCurrentTick();
207 replayCompleted =
true;
209 if (collectDiagnostics)
211 pictureReplayTicks = pictureEnd - pictureStart;
212 backbufferFlushTicks = flushEnd - pictureEnd;
213 replayEnd = flushEnd;
216 long snapshotStartTick = !collectDiagnostics ? 0 : HighResTimer.GetCurrentTick();
218 if (collectDiagnostics)
219 snapshotTicks = HighResTimer.GetCurrentTick() - snapshotStartTick;
226 finally { FrameMailbox.Release(slot); }
228 if (collectDiagnostics)
230 double replayMs = HighResTimer.GetDuration(acquired, replayEnd) * 1000d;
231 double pictureMs = HighResTimer.GetDuration(0, pictureReplayTicks) * 1000d;
232 double flushMs = HighResTimer.GetDuration(0, backbufferFlushTicks) * 1000d;
233 double snapshotMs = HighResTimer.GetDuration(0, snapshotTicks) * 1000d;
234 if (telemetryGeneration != 0)
238 Telemetry!.Record(telemetryGeneration,
"replay.cpu.ms", replayMs);
239 Telemetry.Record(telemetryGeneration,
"picture.cpu.ms", pictureMs);
240 Telemetry.Record(telemetryGeneration,
"backbuffer.flush.cpu.ms", flushMs);
243 Telemetry!.Record(telemetryGeneration,
"snapshot.cpu.ms", snapshotMs);
244 Telemetry.Record(telemetryGeneration,
"snapshot.age.ms", age);
245 Telemetry.Record(telemetryGeneration,
"mailbox.published.lifetime", counters.Published);
246 Telemetry.Record(telemetryGeneration,
"mailbox.dropped.lifetime", counters.Dropped);
247 Telemetry.Record(telemetryGeneration,
"mailbox.slots", counters.InUse);
248 Telemetry.Record(telemetryGeneration,
"snapshot.commands.approximate", commands);
250 diagnostics?.Invoke(
new(0, 0)
252 ReplayMilliseconds = replayMs,
253 PictureReplayMilliseconds = pictureMs,
254 BackbufferFlushMilliseconds = flushMs,
255 SnapshotMilliseconds = snapshotMs,
256 SnapshotAgeMilliseconds = age,
257 PublishedSnapshots = counters.Published,
258 DroppedSnapshots = counters.Dropped,
259 SnapshotSlotsInUse = counters.InUse,
260 SnapshotCommandCount = commands
280 ArgumentNullException.ThrowIfNull(destinationCanvas);
285 if (UsesRenderFrameSnapshots)
287 using var image = ReplayRenderFrameSnapshot();
288 DrawCurrentSurface(destinationCanvas);
292 RenderStateSynchronization.EnterGpuRender();
295 var tick = HighResTimer.GetCurrentTick();
297 RenderToBackbuffer(tick);
302 DrawCurrentSurface(destinationCanvas);
312 RenderStateSynchronization.ExitGpuRender();
329 ArgumentNullException.ThrowIfNull(destinationCanvas);
334 DrawCurrentSurface(destinationCanvas);
338 private void DrawCurrentSurface(SKCanvas canvas)
342 if (presentation.Scale <= 0)
return;
346 canvas.Translate(presentation.DestinationRect.Left, presentation.DestinationRect.Top);
347 canvas.Scale(presentation.Scale);
349 (presentation.Scale != 1f ||
350 presentation.DestinationRect.Left != MathF.Floor(presentation.DestinationRect.Left) ||
351 presentation.DestinationRect.Top != MathF.Floor(presentation.DestinationRect.Top));
352 if (needsLinearSampling)
364 finally { canvas.Restore(); }
398 internal abstract void RenderToBackbuffer(
long tick);
404 internal abstract void PresentBackbufferToAdapter();
422 lock (RenderStateSynchronization.SyncRoot)
424 GC.SuppressFinalize(
this);
440 protected virtual void Dispose(
bool disposing)
445 FrameMailbox.Dispose();
449 RenderSurfaceHostRegistry.Unregister(
this);