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
RenderFrameMailbox.cs
1namespace Gondwana.Rendering;
2
8internal sealed class RenderFrameMailbox : IDisposable
9{
10 internal enum State
11 {
15 Free,
19 Building,
23 Published,
27 Rendering,
31 Releasing
32 }
33
34 internal sealed class Slot
35 {
36 internal RenderFrameSnapshot? Frame { get; set; }
37 internal State Ownership;
38
39 internal Slot() { }
40 }
41
42 private readonly object _gate = new();
43 private readonly Slot[] _slots = [new(), new(), new()];
44 private Slot? _published;
45 private bool _closed;
46 private long _publishedCount;
47 private long _droppedCount;
48
49 internal (long Published, long Dropped, int InUse) Counters
50 {
51 get
52 {
53 lock (_gate)
54 return (_publishedCount, _droppedCount, _slots.Count(s => s.Ownership != State.Free));
55 }
56 }
57
58 internal Slot? TryBeginBuild()
59 {
60 lock (_gate)
61 {
62 if (_closed) return null;
63 if (_slots.Any(s => s.Ownership == State.Building))
64 throw new InvalidOperationException("Only one snapshot producer is supported.");
65 var slot = _slots.FirstOrDefault(s => s.Ownership == State.Free);
66 if (slot is null) return null;
67 slot.Ownership = State.Building;
68 return slot;
69 }
70 }
71
72 // Takes ownership of frame even if the mailbox was closed during construction.
73 internal void Publish(Slot slot, RenderFrameSnapshot frame)
74 {
75 Slot? retired;
76 lock (_gate)
77 {
78 Require(slot, State.Building);
79 slot.Frame = frame;
80 if (_closed)
81 {
82 slot.Ownership = State.Releasing;
83 retired = slot;
84 }
85 else
86 {
87 retired = _published;
88 if (retired is not null)
89 {
90 retired.Ownership = State.Releasing;
91 _droppedCount++;
92 }
93 slot.Ownership = State.Published;
94 _published = slot;
95 _publishedCount++;
96 }
97 }
98 if (retired is not null) Recycle(retired);
99 }
100
101 internal void AbortBuild(Slot slot)
102 {
103 lock (_gate)
104 {
105 Require(slot, State.Building);
106 slot.Ownership = State.Free;
107 }
108 }
109
110 internal Slot? TryAcquire()
111 {
112 lock (_gate)
113 {
114 if (_closed || _published is null) return null;
115 if (_slots.Any(s => s.Ownership == State.Rendering))
116 throw new InvalidOperationException("Only one snapshot consumer is supported.");
117 var slot = _published;
118 _published = null;
119 slot.Ownership = State.Rendering;
120 return slot;
121 }
122 }
123
124 internal void Release(Slot slot)
125 {
126 lock (_gate)
127 {
128 Require(slot, State.Rendering);
129 slot.Ownership = State.Releasing;
130 }
131 Recycle(slot);
132 }
133
134 private void Require(Slot slot, State state)
135 {
136 if (!Array.Exists(_slots, candidate => ReferenceEquals(candidate, slot)) || slot.Ownership != state)
137 throw new InvalidOperationException("Snapshot slot is not owned by this operation.");
138 }
139
140 private void Recycle(Slot slot)
141 {
142 try { slot.Frame?.Dispose(); }
143 finally
144 {
145 lock (_gate)
146 {
147 slot.Frame = null;
148 slot.Ownership = State.Free;
149 }
150 }
151 }
152
156 public void Dispose()
157 {
158 Slot? retired;
159 lock (_gate)
160 {
161 if (_closed) return;
162 _closed = true;
163 retired = _published;
164 _published = null;
165 if (retired is not null) retired.Ownership = State.Releasing;
166 // Building and Rendering belong to their callers until Publish/Abort/Release.
167 }
168 if (retired is not null) Recycle(retired);
169 }
170}
171