diff options
Diffstat (limited to 'src/Godosa.Desktop/GpuTimer.cs')
| -rw-r--r-- | src/Godosa.Desktop/GpuTimer.cs | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/src/Godosa.Desktop/GpuTimer.cs b/src/Godosa.Desktop/GpuTimer.cs new file mode 100644 index 0000000..11076e8 --- /dev/null +++ b/src/Godosa.Desktop/GpuTimer.cs @@ -0,0 +1,71 @@ +using System; +using Silk.NET.OpenGL; + +namespace Godosa.Desktop; + +/// <summary>GPU time per frame from GL_TIME_ELAPSED queries (game-loop spec ยง6). A small +/// ring so reading a result never stalls on the frame still in flight; disables itself +/// when the context has no timer queries (e.g. GLES without the extension).</summary> +public sealed class GpuTimer +{ + private const int Ring = 4; + private readonly uint[] _queries = new uint[Ring]; + private readonly bool[] _pending = new bool[Ring]; + private int _current; + private bool _open; + + public bool Supported { get; private set; } + + public void Init(GL gl) + { + try + { + string version = gl.GetStringS(StringName.Version) ?? ""; + if (version.Contains("OpenGL ES", StringComparison.Ordinal)) return; + for (int i = 0; i < Ring; i++) _queries[i] = gl.GenQuery(); + Supported = gl.GetError() == GLEnum.NoError; + } + catch (Exception) + { + Supported = false; + } + } + + public void Begin(GL gl) + { + if (!Supported || _pending[_current]) return; // slot still in flight: skip this frame + gl.BeginQuery(QueryTarget.TimeElapsed, _queries[_current]); + _open = true; + } + + public void End(GL gl) + { + if (!_open) return; + gl.EndQuery(QueryTarget.TimeElapsed); + _pending[_current] = true; + _open = false; + _current = (_current + 1) % Ring; + } + + /// <summary>Seconds for each finished query since the last call.</summary> + public void Collect(GL gl, Action<double> onResult) + { + if (!Supported) return; + for (int i = 0; i < Ring; i++) + { + if (!_pending[i]) continue; + gl.GetQueryObject(_queries[i], QueryObjectParameterName.ResultAvailable, out int available); + if (available == 0) continue; + gl.GetQueryObject(_queries[i], QueryObjectParameterName.Result, out ulong nanoseconds); + _pending[i] = false; + onResult(nanoseconds / 1e9); + } + } + + public void Delete(GL gl) + { + if (!Supported) return; + for (int i = 0; i < Ring; i++) gl.DeleteQuery(_queries[i]); + Supported = false; + } +} |
