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
AvaloniaGpuRenderSurfaceControl.cs
1using Avalonia.OpenGL;
2using Avalonia.OpenGL.Controls;
5using SkiaSharp;
6
8
31public class AvaloniaGpuRenderSurfaceControl : OpenGlControlBase, IDisposable
32{
33 // GL_RGBA8 pixel format for the SkiaSharp framebuffer wrapper.
34 private const uint GlRgba8 = 0x8058;
35
36 private GRGlInterface? _glInterface;
37 private GRContext? _grContext;
38 private GpuBackbuffer? _gpuBackbuffer;
39 private readonly object _lifecycleGate = new();
40 private int _disposeRequested;
41
42 // Last physical dimensions used to detect resize inside OnOpenGlRender.
43 private int _lastPhysW;
44 private int _lastPhysH;
45
50
54 public RenderSurfaceHost<GpuBackbuffer> Host { get; }
55
60 {
61 var scaling = VisualRoot?.RenderScaling ?? 1.0;
62 var physW = Math.Max(1, (int)Math.Round(Bounds.Width * scaling));
63 var physH = Math.Max(1, (int)Math.Round(Bounds.Height * scaling));
64
65 Adapter = new AvaloniaGpuRenderSurfaceAdapter(physW, physH);
66 Host = new RenderSurfaceHost<GpuBackbuffer>(Adapter);
67 _gpuBackbuffer = (GpuBackbuffer)Host.Backbuffer;
68
69 // Pacing: post RequestNextFrameRendering() after each engine foreground cycle.
70 Adapter.AttachToEngine(RequestNextFrameRendering);
71 }
72
73 // ── OpenGL lifecycle callbacks (called on the GL thread by Avalonia) ─────
74
76 protected override void OnOpenGlInit(GlInterface gl)
77 {
78 // Create a SkiaSharp GRContext from the current OpenGL context.
79 // GRGlInterface.Create() auto-detects the current platform GL/EGL/WGL context
80 // (which Avalonia has made current before this callback) and loads function
81 // pointers from it without requiring any explicit proc-address lookup delegate.
82 lock (_lifecycleGate)
83 {
84 if (_disposeRequested != 0) return;
85 _glInterface = GRGlInterface.Create();
86 if (_glInterface == null) return;
87 _grContext = GRContext.CreateGl(_glInterface);
88 if (_grContext == null)
89 {
90 _glInterface.Dispose();
91 _glInterface = null;
92 return;
93 }
94 }
95
96 var scaling = VisualRoot?.RenderScaling ?? 1.0;
97 var physW = (int)Math.Round(Bounds.Width * scaling);
98 var physH = (int)Math.Round(Bounds.Height * scaling);
99
100 if (physW <= 0 || physH <= 0) return;
101
102 _lastPhysW = physW;
103 _lastPhysH = physH;
104
105 Adapter.UpdateDimensions(physW, physH);
106 _gpuBackbuffer?.EnsureInitialized(_grContext);
107 }
108
110 protected override void OnOpenGlRender(GlInterface gl, int fb)
111 {
112 long telemetryGeneration = Host?.Telemetry?.BeginSample() ?? 0;
113 if (Volatile.Read(ref _disposeRequested) != 0 || _grContext == null || _gpuBackbuffer == null) return;
114
115 // Notify SkiaSharp that external GL code (Avalonia's compositor) may have
116 // modified the GL state since the last Skia draw call.
117 _grContext.ResetContext();
118
119 var scaling = VisualRoot?.RenderScaling ?? 1.0;
120 var physW = (int)Math.Round(Bounds.Width * scaling);
121 var physH = (int)Math.Round(Bounds.Height * scaling);
122
123 if (physW <= 0 || physH <= 0)
124 {
125 _lastPhysW = _lastPhysH = 0;
126 Adapter.UpdateDimensions(0, 0);
127 return;
128 }
129
130 // Resize presentation resources only; the logical render target remains unchanged.
131 if (physW != _lastPhysW || physH != _lastPhysH)
132 {
133 _lastPhysW = physW;
134 _lastPhysH = physH;
135 Adapter.UpdateDimensions(physW, physH);
136
137 }
138
139 _gpuBackbuffer.EnsureInitialized(_grContext);
140
141 // Wrap the Avalonia-provided framebuffer in a SkiaSharp surface for compositing.
142 // The framebuffer uses OpenGL bottom-left origin, RGBA8 color format.
143 var fbInfo = new GRGlFramebufferInfo((uint)fb, GlRgba8);
144 using var renderTarget = new GRBackendRenderTarget(physW, physH, sampleCount: 0, stencilBits: 0, fbInfo);
145 using var fbSurface = SKSurface.Create(_grContext, renderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);
146
147 if (fbSurface == null) return;
148
149 var canvas = fbSurface.Canvas;
150
151 // Replay the newest immutable Engine snapshot on GL and blit the result.
152 using var img = Host.GlRenderAndSnapshot();
153 if (img != null)
154 {
155 Adapter.DrawImage(canvas, img, Adapter.ClearColor);
156 }
157 else
158 {
159 canvas.Clear(Adapter.ClearColor);
160 }
161
162 _grContext.Flush();
163 _gpuBackbuffer.RecordFrame();
164 if (telemetryGeneration != 0) Host!.Telemetry!.Record(telemetryGeneration, "presentation.count", 1);
165 }
166
168 protected override void OnOpenGlDeinit(GlInterface gl)
169 {
170 lock (_lifecycleGate)
171 {
172 // Avalonia may deinitialize and recreate GL without ending the game's
173 // logical lifecycle. Release surfaces before their owning context.
174 _gpuBackbuffer?.ReleaseContext();
175 _grContext?.Dispose();
176 _grContext = null;
177 _glInterface?.Dispose();
178 _glInterface = null;
179 if (_disposeRequested != 0) _gpuBackbuffer?.Dispose();
180 }
181 }
182
188 public void Dispose()
189 {
190 if (Interlocked.Exchange(ref _disposeRequested, 1) != 0) return;
191 Host.Dispose();
192 Adapter.Dispose();
193 lock (_lifecycleGate)
194 {
195 // If GL is already gone (or never existed), only CPU resources remain.
196 if (_grContext is null) _gpuBackbuffer?.Dispose();
197 }
198 GC.SuppressFinalize(this);
199 }
200}
Provides a GPU-accelerated render surface adapter for Avalonia, backed by global::Avalonia....
void Dispose()
Ends the logical surface lifecycle. The hosting class calls this on shutdown; custom hosts should cal...
AvaloniaGpuRenderSurfaceAdapter Adapter
Gets the GPU render surface adapter used to drive this control.
RenderSurfaceHost< GpuBackbuffer > Host
Gets the RenderSurfaceHost<T> bound to this control.
AvaloniaGpuRenderSurfaceControl()
Initializes a new instance of the AvaloniaGpuRenderSurfaceControl class.
Provides a fully GPU-rendered backbuffer implementation using SkiaSharp and OpenGL.