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
BlazorGpuRenderSurfaceComponent.razor.cs
1using System.Runtime.Versioning;
4using Microsoft.JSInterop;
5using SkiaSharp;
6using SkiaSharp.Views.Blazor;
7
9
33[SupportedOSPlatform("browser")]
35{
36 private readonly string _generatedCanvasId = $"gondwana-webgl-{Guid.NewGuid():N}";
37 private BlazorSkiaGlView? _glView;
38 private IJSObjectReference? _module;
39 private GRContext? _grContext;
40 private IReadOnlyDictionary<string, object>? _gpuCanvasAttributes;
41 private string _canvasId = string.Empty;
42 private bool _backbufferNeedsRender = true;
43 private bool _disposed;
44
46 public BlazorGpuRenderSurfaceAdapter Adapter { get; private set; } = null!;
47
49 public RenderSurfaceHost<GpuBackbuffer> Host { get; private set; } = null!;
50
52 private IReadOnlyDictionary<string, object>? GpuCanvasAttributes => _gpuCanvasAttributes;
53
54 internal override ValueTask<CanvasOffset> GetCanvasOffsetAsync()
55 => _module is null ? ValueTask.FromResult(default(CanvasOffset))
56 : _module.InvokeAsync<CanvasOffset>("getCanvasOffset", _canvasId);
57
58 internal override RenderSurfaceAdapterBase? InputSurfaceAdapter => Adapter;
59
61 protected override void OnInitialized()
62 {
64 Host = new RenderSurfaceHost<GpuBackbuffer>(Adapter);
65 Adapter.AttachToEngine();
66 }
67
69 protected override void OnParametersSet()
70 {
71 base.OnParametersSet();
72
73 var attributes = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
74 if (CanvasAttributes is not null)
75 {
76 foreach (var attribute in CanvasAttributes)
77 attributes[attribute.Key] = attribute.Value;
78 }
79
80 if (attributes.TryGetValue("id", out var id) && id is not null)
81 _canvasId = id.ToString() ?? _generatedCanvasId;
82 else
83 attributes["id"] = _canvasId = _generatedCanvasId;
84
85 attributes[nameof(SKGLView.OnPaintSurface)] =
86 (Action<SKPaintGLSurfaceEventArgs>)HandlePaintSurface;
87
88 _gpuCanvasAttributes = attributes;
89 }
90
92 protected override async Task OnAfterRenderAsync(bool firstRender)
93 {
94 if (!firstRender)
95 return;
96
97 _module = await JS.InvokeAsync<IJSObjectReference>(
98 "import",
99 "./_content/Gondwana.Blazor/gondwana-blazor.js");
100
101 try
102 {
103 await _module.InvokeVoidAsync("focusElementById", _canvasId);
104 await _module.InvokeVoidAsync("suppressBrowserInputDefaultsById", _canvasId);
105 }
106 catch
107 {
108 // Focus is best-effort; browser policy may reject programmatic focus.
109 }
110 }
111
112 private void HandlePaintSurface(SKPaintGLSurfaceEventArgs e)
113 {
114 long telemetryGeneration = Host?.Telemetry?.BeginSample() ?? 0;
115 if (_disposed)
116 return;
117
118 var width = e.Info.Width;
119 var height = e.Info.Height;
120 Adapter.BeginPaint(width, height);
121
122 if (width <= 0 || height <= 0 || e.Surface.Context is not GRContext grContext)
123 {
124 e.Surface.Canvas.Clear(SKColors.Black);
125 return;
126 }
127
128 var backbuffer = (GpuBackbuffer)Host.Backbuffer;
129 _grContext = grContext;
130 if (backbuffer.EnsureInitialized(grContext))
131 _backbufferNeedsRender = true;
132
133 var engine = Engine.Instance;
134 if (!engine.IsRunning)
135 {
136 e.Surface.Canvas.Clear(backbuffer.ClearColor);
137 return;
138 }
139
140 // SKGLView owns the browser rAF for the GPU path. Advance simulation first so a
141 // foreground frame request, when due, can be rendered in this same paint callback.
142 engine.Tick();
143
144 bool engineFrameRequested = Adapter.ConsumeFrameRequest();
145 bool renderScene = _backbufferNeedsRender || engineFrameRequested;
146
147 bool presented = renderScene
148 ? Host.GlRenderToCanvas(e.Surface.Canvas)
149 : Host.GlDrawCurrentFrameToCanvas(e.Surface.Canvas);
150
151 if (!presented)
152 {
153 e.Surface.Canvas.Clear(backbuffer.ClearColor);
154 return;
155 }
156
157 if (renderScene)
158 _backbufferNeedsRender = false;
159
160 // Count successful WebGL paint/presentation callbacks. This intentionally measures the
161 // browser GPU presentation cadence, which may differ from the engine's TargetFPS.
162 backbuffer.RecordFrame();
163 if (telemetryGeneration != 0) Host!.Telemetry!.Record(telemetryGeneration, "presentation.count", 1);
164 }
165
167 public override void Dispose()
168 {
169 if (_disposed)
170 return;
171
172 _disposed = true;
173
174 Adapter?.Dispose();
175 Host?.Dispose();
176
177 _grContext = null;
178 _glView = null;
179
180 if (_module is not null)
181 {
182 _ = _module.DisposeAsync();
183 _module = null;
184 }
185 }
186}
Coordinates Gondwana foreground-frame requests with a Blazor WebGL render surface.
A Blazor WebGL render surface that draws Gondwana scenes through an GpuBackbuffer without transferrin...
RenderSurfaceHost< GpuBackbuffer > Host
Gets the GPU render-surface host bound to this component.
BlazorGpuRenderSurfaceAdapter Adapter
Gets the adapter that coordinates engine frame requests with this component.
Provides the shared DOM input surface used by Gondwana's Blazor bitmap and GPU render components.
IReadOnlyDictionary< string, object >? CanvasAttributes
Gets the canvas attributes with the component-owned style attribute removed.
Gives Razor a local component name for SkiaSharp's WebGL view. This avoids Razor resolving SkiaSharp ...
Represents the core game engine singleton responsible for managing the main update loop,...
Definition Engine.cs:36
void Tick()
Advances the timer-driven engine by one externally presented frame.
Definition Engine.cs:649
static Engine Instance
Gets the singleton instance of the Engine.
Definition Engine.cs:49
Provides a fully GPU-rendered backbuffer implementation using SkiaSharp and OpenGL.
Represents the abstract base class for render surface adapters that present backbuffer output to a de...
override BackbufferBase Backbuffer
Gets the backbuffer used for off-screen rendering before presentation to the adapter.
Diagnostics.? TelemetrySource Telemetry
Neutral session-local measurements for this host; null when source capacity is exhausted or the share...
bool GlDrawCurrentFrameToCanvas(SKCanvas destinationCanvas)
Draws the existing GPU backbuffer surface directly to another GPU canvas without rendering a new scen...
bool GlRenderToCanvas(SKCanvas destinationCanvas)
Renders the current scene frame and draws the GPU backbuffer surface directly to another GPU canvas....