• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DemoBytom / DemoEngine / 24746555599

21 Apr 2026 09:07PM UTC coverage: 30.388% (-0.2%) from 30.6%
24746555599

push

coveralls.net

DemoBytom
WIP new WindowsMessagePump

1283 of 4222 relevant lines covered (30.39%)

0.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.79
/src/Demo.Engine.Core/Services/MainLoopService.cs
1
// Copyright © Michał Dembski and contributors.
2
// Distributed under MIT license. See LICENSE file in the root for more information.
3

4
using System.Diagnostics;
5
using Demo.Engine.Core.Features.StaThread;
6
using Demo.Engine.Core.Interfaces;
7
using Demo.Engine.Core.Interfaces.Rendering;
8
using Demo.Engine.Core.Requests.Keyboard;
9
using Demo.Engine.Core.ValueObjects;
10
using Mediator;
11
using Microsoft.Extensions.Logging;
12

13
namespace Demo.Engine.Core.Services;
14

15
internal sealed class MainLoopService
16
    : IMainLoopService,
17
      IAsyncDisposable
18
{
19
    private readonly ILogger<MainLoopService> _logger;
20
    private readonly IStaThreadWriter _staThreadWriter;
21
    private readonly IMediator _mediator;
22
    private readonly IFpsTimer _fpsTimer;
23
    private readonly IMainLoopLifetime _mainLoopLifetime;
24
    private readonly ILoopJob _loopJob;
25
    private bool _disposedValue;
26

27
    public Task ExecutingTask { get; }
28

29
    public MainLoopService(
1✔
30
        ILogger<MainLoopService> logger,
1✔
31
        IStaThreadWriter staThreadWriter,
1✔
32
        IMediator mediator,
1✔
33
        IFpsTimer fpsTimer,
1✔
34
        IRenderingEngine renderingEngine,
1✔
35
        IMainLoopLifetime mainLoopLifetime,
1✔
36
        ILoopJob loopJob)
1✔
37
    {
38
        _logger = logger;
1✔
39
        _staThreadWriter = staThreadWriter;
1✔
40
        _mediator = mediator;
1✔
41
        _fpsTimer = fpsTimer;
1✔
42
        _mainLoopLifetime = mainLoopLifetime;
1✔
43
        _loopJob = loopJob;
1✔
44
        ExecutingTask = Task.Run(async () =>
1✔
45
        {
1✔
46
            try
1✔
47
            {
1✔
48
                await Task.WhenAll(
1✔
49
                    DoAsync(renderingEngine)/*,
1✔
50
                    DoEventsAsync(renderingEngine)*/);
1✔
51
            }
1✔
52
            catch (TaskCanceledException)
×
53
            {
1✔
54
                _mainLoopLifetime.Cancel();
×
55
            }
×
56
            catch (Exception ex)
×
57
            {
1✔
58
                _logger.LogMainLoopFailedWithError(ex);
×
59
                _mainLoopLifetime.Cancel();
×
60
                //throw;
1✔
61
            }
×
62
        });
1✔
63
    }
1✔
64

65
    private async Task DoAsync(
66
        IRenderingEngine renderingEngine)
67
    {
68
        var keyboardHandle = await _mediator.Send(new KeyboardHandleRequest(), CancellationToken.None);
1✔
69
        var keyboardCharCache = await _mediator.Send(new KeyboardCharCacheRequest(), CancellationToken.None);
1✔
70

71
        //await Task.Delay(10_000);
72
        var surfaces = new List<RenderingSurfaceId>
1✔
73
        {
1✔
74
            await _staThreadWriter.CreateSurface(
1✔
75
                renderingEngine,
1✔
76
                _mainLoopLifetime.Token),
1✔
77
            //await _staThreadWriter.CreateSurface(
1✔
78
            //    _mainLoopLifetime.Token),
1✔
79
        };
1✔
80

81
        if (surfaces is [var mainFormId, ..]
1✔
82
            && renderingEngine.TryGetRenderingSurface(mainFormId, out var mainForm))
1✔
83
        {
84
            mainForm.RenderingControl.RenderingFormClosed += (_, _) => _mainLoopLifetime.Cancel();
1✔
85
        }
86

87
        var previous = Stopwatch.GetTimestamp();
1✔
88
        var lag = TimeSpan.Zero;
1✔
89

90
        var msPerUpdate = TimeSpan.FromSeconds(1) / 60;
1✔
91

92
        var doEventsOk = true;
1✔
93
        var cnt = 0;
1✔
94
        while (
1✔
95
            doEventsOk
1✔
96
            //&& IsRunning
1✔
97
            && !_disposedValue
1✔
98
            && !_mainLoopLifetime.Token.IsCancellationRequested)
1✔
99
        {
100
            var current = Stopwatch.GetTimestamp();
1✔
101
            var elapsed = Stopwatch.GetElapsedTime(previous, current);
1✔
102
            previous = current;
1✔
103
            lag += elapsed;
1✔
104

105
            //process input
106
            // TODO!
107

108
            while (lag >= msPerUpdate)
1✔
109
            {
110
                //Update
111
                // TODO - fix the UPS timer.. somehow :D
112
                _fpsTimer.StopUpdateTimer();
1✔
113
                RenderingSurfaceId? sId = null;
1✔
114

115
                ++cnt;
1✔
116
                foreach (var renderingSurfaceId in surfaces)
1✔
117
                {
118
                    if (!renderingEngine.TryGetRenderingSurface(
119
                        renderingSurfaceId,
1✔
120
                        out var renderingSurface))
1✔
121
                    {
122
                        _logger.LogRenderingSurfaceNotFound(
×
123
                            renderingSurfaceId);
×
124
                        break;
×
125
                    }
126

127
                    await _loopJob.Update(
1✔
128
                          renderingSurface,
1✔
129
                          keyboardHandle,
1✔
130
                          keyboardCharCache);
1✔
131

132
                    if (cnt == 10 * 60)
133
                    {
134
                        sId = await renderingSurface.RenderingControl.CreateSurface(
×
135
                            renderingEngine,
×
136
                            _mainLoopLifetime.Token);
×
137

138
                        cnt = 0;
×
139
                    }
140
                }
1✔
141
                if (sId is not null)
142
                {
143
                    surfaces.Add(sId.Value);
×
144
                    sId = null;
×
145
                }
146
                lag -= msPerUpdate;
1✔
147
                _fpsTimer.StartUpdateTimer();
1✔
148
            }
149

150
            //Render
151
            foreach (var renderingSurfaceId in surfaces)
1✔
152
            {
153
                //doEventsOk &= await _staThreadWriter.DoEventsOk(
154
                //    renderingSurfaceId,
155
                //    _mainLoopLifetime.Token);
156

157
                using var scope = _fpsTimer.StartRenderingTimerScope(
1✔
158
                    renderingSurfaceId);
1✔
159

160
                _loopJob.Render(
1✔
161
                    renderingEngine,
1✔
162
                    renderingSurfaceId);
1✔
163
            }
164
            //await Task.Delay(15).ConfigureAwait(true);
165
        }
166
        _mainLoopLifetime.Cancel();
1✔
167
    }
1✔
168

169
    private long _doEventsCnt = 0;
170
    private async Task DoEventsAsync(
171
        IRenderingEngine renderingEngine)
172
    {
173
        var doEventsOk = true;
×
174
        try
175
        {
176
            do
177
            {
178
                var surfaces = renderingEngine.RenderingSurfaces;
×
179
                foreach (var renderingSurfaceId in surfaces)
×
180
                {
181
                    _doEventsCnt += 1;
×
182
                    doEventsOk &= await _staThreadWriter.BlockingDoEventsOk(
×
183
                        renderingEngine,
×
184
                        null!,
×
185
                        renderingSurfaceId.ID,
×
186
                        _mainLoopLifetime.Token);
×
187
                }
188
            } while (doEventsOk
×
189
                // Currently the unit test relies on those conditions
×
190
                && !_disposedValue
×
191
                && !_mainLoopLifetime.Token.IsCancellationRequested);
×
192
        }
×
193
        finally
194
        {
195
#pragma warning disable CA1873 // Avoid potentially expensive logging
196
            _logger.LogInformation("Called DoEvents {Count} times", _doEventsCnt);
×
197
#pragma warning restore CA1873 // Avoid potentially expensive logging
198
            _mainLoopLifetime.Cancel();
×
199
        }
200
    }
×
201

202
    private async ValueTask Dispose(bool disposing)
203
    {
204
        if (!_disposedValue)
1✔
205
        {
206
            if (disposing)
1✔
207
            {
208
            }
209

210
            _disposedValue = true;
1✔
211
            //Make sure the loop finishes
212
            await ExecutingTask;
1✔
213
        }
214
    }
1✔
215

216
    public async ValueTask DisposeAsync()
217
    {
218
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
219
        await Dispose(disposing: true);
1✔
220
        GC.SuppressFinalize(this);
1✔
221
    }
1✔
222
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc