42 private SKBitmap? _cpuBitmap;
43 private SKSurface? _surface;
44 private bool _disposed;
45 private GRContext? _context;
46 private sealed record Resolution(
int Width,
int Height);
47 private Resolution? _requestedResolution;
49 private int _targetFps = 60;
50 private readonly
object _msaaSyncRoot =
new();
51 private int _msaaSampleCount = 1;
52 private long _msaaConfigurationRevision;
53 private long _appliedMsaaConfigurationRevision = -1;
54 private int _actualMsaaSampleCount = 1;
55 private int _maxSupportedMsaaSampleCount = 1;
59 private long _frameCount;
76 set => _targetFps = value < 0 ? 0 : value;
99 public bool VSync {
get;
set; } =
true;
134 return _msaaSampleCount;
138 int normalized = value < 1 ? 1 : value;
142 if (_msaaSampleCount == normalized)
145 _msaaSampleCount = normalized;
146 _msaaConfigurationRevision++;
175 internal bool IsMsaaSurfaceRecreationPending =>
176 Volatile.Read(ref _appliedMsaaConfigurationRevision) !=
177 Volatile.Read(ref _msaaConfigurationRevision);
185 : base(width, height)
191 VSync = config.VSync;
194 CreateCpuSurface(width, height);
215 public void Initialize(GRContext grContext,
int width,
int height)
217 if (width <= 0 || height <= 0)
219 if (_disposed)
return;
221 (
int requestedSampleCount,
long configurationRevision) = GetMsaaConfigurationSnapshot();
222 int maxSupportedSampleCount = grContext.GetMaxSurfaceSampleCount(SKColorType.Rgba8888);
223 Volatile.Write(ref _maxSupportedMsaaSampleCount, maxSupportedSampleCount);
226 int actualSampleCount = CreateGpuSurface(grContext, width, height, requestedSampleCount);
227 _context = grContext;
230 lock (RenderStateSynchronization.SyncRoot)
233 MarkMsaaConfigurationApplied(configurationRevision, actualSampleCount);
245 protected internal override void RequestResize(
int width,
int height)
246 => Interlocked.Exchange(ref _requestedResolution,
new(width, height));
257 ArgumentNullException.ThrowIfNull(context);
258 if (_disposed)
return false;
259 var request = Interlocked.Exchange(ref _requestedResolution,
null);
260 int width = request?.Width ??
Width;
261 int height = request?.Height ??
Height;
262 if (ReferenceEquals(_context, context) &&
265 !IsMsaaSurfaceRecreationPending)
277 public override SKCanvas
Canvas => _surface?.Canvas
278 ??
throw new InvalidOperationException($
"{nameof(GpuBackbuffer)} surface is not initialized.");
283 protected internal override void BeginFrame()
285 if (_disposed || _surface is
null)
return;
286 var c = _surface.Canvas;
296 protected internal override void EndFrame()
298 if (_disposed || _surface is
null)
return;
307 protected internal override void DrawTileFrame(
Tile tile, RectangleF destRectScreen)
310 if (image is
null || _surface is
null)
return;
312 var canvas = _surface.Canvas;
314 canvas.DrawImage(image, destRectScreen.ToSKRect());
327 protected internal override SKImage Snapshot()
329 if (_disposed || _surface is
null)
332 return _surface.Snapshot();
341 public void RecordFrame() => Interlocked.Increment(ref _frameCount);
348 internal long ConsumeFrameCount() => Interlocked.Exchange(ref _frameCount, 0);
352 internal (
int SampleCount,
long Revision) GetMsaaConfigurationSnapshot()
355 return (_msaaSampleCount, _msaaConfigurationRevision);
358 internal void MarkMsaaConfigurationApplied(
long revision,
int actualSampleCount)
360 Volatile.Write(ref _actualMsaaSampleCount, actualSampleCount);
361 Volatile.Write(ref _appliedMsaaConfigurationRevision, revision);
364 private int CreateGpuSurface(
368 int requestedSampleCount)
371 var info =
new SKImageInfo(width, height, SKColorType.Rgba8888, SKAlphaType.Premul);
372 _surface = SKSurface.Create(grContext, budgeted:
true, info, requestedSampleCount);
374 if (_surface is not
null)
375 return requestedSampleCount;
380 if (requestedSampleCount > 1)
381 _surface = SKSurface.Create(grContext, budgeted:
true, info, sampleCount: 1);
383 if (_surface is
null)
384 throw new InvalidOperationException(
385 $
"Failed to create a {nameof(GpuBackbuffer)} GPU surface ({width}x{height}). " +
386 "The GRContext may be invalid or the pixel format is not renderable.");
391 private void CreateCpuSurface(
int width,
int height)
395 var info =
new SKImageInfo(width, height, SKColorType.Bgra8888, SKAlphaType.Premul);
396 _cpuBitmap =
new SKBitmap(info);
397 _surface = SKSurface.Create(info, _cpuBitmap.GetPixels(), _cpuBitmap.Info.RowBytes);
400 private void DisposeSurface()
404 _cpuBitmap?.Dispose();
411 internal void ReleaseContext()
422 if (_disposed)
return;
426 GC.SuppressFinalize(
this);