Gondwana Game Engine Development (master) - 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
VideoFrameMailbox.cs
1using System.Runtime.InteropServices;
2using SkiaSharp;
3
4namespace Gondwana.Video;
5
6// Decoder callbacks own only this mailbox. Skia and engine state belong to Update/Draw.
7internal sealed class VideoFrameMailbox : IDisposable
8{
9 private readonly object _gate = new();
10 private byte[] _pixels = [];
11 private int _width, _height;
12 private bool _pending, _clear, _disposed;
13
14 internal void Publish(VideoFrameReadyEventArgs frame)
15 {
16 lock (_gate)
17 {
18 if (_disposed) return;
19 int rowBytes = checked(frame.Width * 4);
20 int length = checked(rowBytes * frame.Height);
21 if (frame.Width <= 0 || frame.Height <= 0 || frame.Stride < rowBytes || frame.Pixels == IntPtr.Zero)
22 throw new ArgumentException("Invalid video frame dimensions, stride, or pointer.", nameof(frame));
23 if (_pixels.Length != length) _pixels = new byte[length];
24 for (int y = 0; y < frame.Height; y++)
25 {
26 Marshal.Copy(IntPtr.Add(frame.Pixels, checked(y * frame.Stride)), _pixels, y * rowBytes, rowBytes);
27 // RV32's X byte is undefined. Skia raster copies can preserve that byte even
28 // with Opaque metadata, so establish real opaque BGRA without swapping RGB.
29 for (int x = y * rowBytes + 3; x < (y + 1) * rowBytes; x += 4) _pixels[x] = 255;
30 }
31 _width = frame.Width;
32 _height = frame.Height;
33 _pending = true;
34 }
35 }
36
37 internal void Reset()
38 {
39 lock (_gate) { _pending = false; _clear = true; }
40 }
41
42 // Called only by the engine thread. Holding the gate for the copy bounds storage to
43 // one reusable pending buffer and one render-owned bitmap, without per-frame arrays.
44 internal bool Consume(ref SKBitmap? bitmap)
45 {
46 lock (_gate)
47 {
48 if (_disposed) return false;
49 bool changed = _clear;
50 if (_clear) { bitmap?.Dispose(); bitmap = null; _clear = false; }
51 if (!_pending) return changed;
52 if (bitmap is null || bitmap.Width != _width || bitmap.Height != _height)
53 {
54 bitmap?.Dispose();
55 bitmap = new SKBitmap(new SKImageInfo(_width, _height, SKColorType.Bgra8888, SKAlphaType.Opaque));
56 }
57 int rowBytes = checked(_width * 4);
58 for (int y = 0; y < _height; y++)
59 Marshal.Copy(_pixels, y * rowBytes, IntPtr.Add(bitmap.GetPixels(), y * bitmap.RowBytes), rowBytes);
60 bitmap.NotifyPixelsChanged();
61 _pending = false;
62 return true;
63 }
64 }
65
69 public void Dispose()
70 {
71 lock (_gate) { _disposed = true; _pending = false; _pixels = []; }
72 }
73}