-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDisplayEngineTask.h
More file actions
321 lines (293 loc) · 7.64 KB
/
DisplayEngineTask.h
File metadata and controls
321 lines (293 loc) · 7.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#ifndef _GRAPHICS_DUAL_BUFFER_TASK_h
#define _GRAPHICS_DUAL_BUFFER_TASK_h
#define _TASK_OO_CALLBACKS
#include <TSchedulerDeclarations.hpp>
#include "DisplaySyncType.h"
#include "AsyncBufferPushTask.h"
namespace Egfx
{
template<typename FramebufferType, typename ScreenDriverType>
class DisplayEngineTask : public IFrameEngine, private TS::Task
{
private:
enum class StateEnum : uint8_t
{
WaitForScreenStart,
Clear,
ClearEnd,
Render,
Sync,
PushBuffer,
WaitingForPush,
FinalizeFrame
};
private:
// Display timing information. Performance objects have a local state and a read-only copy.
#if defined(EGFX_PERFORMANCE_LOG)
uint32_t MeasureTimestamp = 0;
#if defined(EGFX_PERFORMANCE_LOG_DETAIL)
uint32_t StepTimestamp = 0;
DisplayPerformanceDetailStruct FrameTiming{};
DisplayPerformanceDetailStruct FrameTimingCopy{};
#else
DisplayPerformanceStruct FrameTiming{};
DisplayPerformanceStruct FrameTimingCopy{};
#endif
#else
DisplayTimingsStruct FrameTiming{};
DisplayTimingsStruct FrameTimingCopy{};
#endif
private:
AsyncBufferPushTask<ScreenDriverType> BufferPusher;
private:
FramebufferType& FrameBuffer;
ScreenDriverType& ScreenDriver;
IFrameDraw* Drawer = nullptr;
private:
uint32_t SyncReference = 0;
StateEnum State = StateEnum::WaitForScreenStart;
DisplaySyncType SyncType = DisplaySyncType::Vrr;
public:
DisplayEngineTask(TS::Scheduler& scheduler,
FramebufferType& framebuffer,
ScreenDriverType& screenDriver,
const uint32_t targetPeriodMicros = 16666)
: IFrameEngine()
, TS::Task(TASK_IMMEDIATE, TASK_FOREVER, &scheduler, false)
, BufferPusher(scheduler, screenDriver)
, FrameBuffer(framebuffer)
, ScreenDriver(screenDriver)
{
SetTargetPeriod(targetPeriodMicros);
}
void SetTargetPeriod(const uint32_t targetPeriodMicros)
{
FrameTiming.TargetDuration = targetPeriodMicros;
}
void SetSyncType(const DisplaySyncType syncType)
{
SyncType = syncType;
}
void SetBufferTaskCallback(void (*taskCallback)(void* parameter))
{
BufferPusher.SetBufferTaskCallback(taskCallback);
}
void BufferTaskCallback(void* parameter)
{
BufferPusher.BufferTaskCallback(parameter);
}
public: // IFrameEngine implementation.
void SetDrawer(IFrameDraw* drawer) final
{
if (drawer != nullptr)
{
Drawer = drawer;
if (State == StateEnum::Render)
{
State = StateEnum::WaitForScreenStart;
}
}
}
void SetBrightness(const uint8_t /*brightness*/) final
{
//TODO: integrate with ScreenDriver brightness control.
}
bool Start() final
{
if (FramebufferType::PhysicalWidth == ScreenDriver.GetScreenWidth()
&& FramebufferType::PhysicalHeight == ScreenDriver.GetScreenHeight()
&& ScreenDriver.GetScreenWidth() > 0
&& ScreenDriver.GetScreenHeight() > 0)
{
if (ScreenDriver.Start())
{
TS::Task::enableDelayed(0);
TS::Task::forceNextIteration();
State = StateEnum::WaitForScreenStart;
return true;
}
}
Stop();
return false;
}
void Stop() final
{
ScreenDriver.Stop();
TS::Task::disable();
}
virtual void GetDisplayTimings(DisplayTimingsStruct& timings) const
{
timings = FrameTimingCopy;
}
#if defined(EGFX_PERFORMANCE_LOG)
virtual void GetDisplayPerformance(DisplayPerformanceStruct& timings) const
{
timings = FrameTimingCopy;
}
#if defined(EGFX_PERFORMANCE_LOG_DETAIL)
virtual void GetDisplayPerformanceDetail(DisplayPerformanceDetailStruct& timings) const
{
timings = FrameTimingCopy;
}
#endif
#endif
public:
bool Callback() final
{
switch (State)
{
case StateEnum::WaitForScreenStart:
if (ScreenDriver.CanPushBuffer())
{
State = StateEnum::Clear;
FrameTiming.FrameCounter = UINT16_MAX; // Clear step will increment to 0.
#if defined(EGFX_PERFORMANCE_LOG)
MeasureTimestamp = micros();
SyncReference = MeasureTimestamp - FrameTiming.TargetDuration + 1;
#else
SyncReference = micros() - FrameTiming.TargetDuration + 1;
#endif
}
break;
case StateEnum::Clear:
#if defined(EGFX_PERFORMANCE_LOG) && defined(EGFX_PERFORMANCE_LOG_DETAIL)
StepTimestamp = micros();
#endif
if (FrameBuffer.ClearFrameBuffer())
{
State = StateEnum::Render;
FrameTiming.FrameCounter++;
FrameTiming.FrameTimestamp = micros();
#if defined(EGFX_PERFORMANCE_LOG)
FrameTiming.ClearDuration = FrameTiming.FrameTimestamp - MeasureTimestamp;
#endif
}
break;
case StateEnum::Render:
if (Drawer == nullptr
|| Drawer->DrawCall(&FrameBuffer, FrameTiming.FrameTimestamp, FrameTiming.FrameCounter))
{
State = StateEnum::Sync;
#if defined(EGFX_PERFORMANCE_LOG)
MeasureTimestamp = micros();
FrameTiming.RenderDuration = MeasureTimestamp - FrameTiming.FrameTimestamp;
#endif
}
break;
case StateEnum::Sync:
if (Sync(micros()))
{
State = StateEnum::PushBuffer;
#if defined(EGFX_PERFORMANCE_LOG)
FrameTiming.SyncDuration = micros() - MeasureTimestamp;
#endif
}
break;
case StateEnum::PushBuffer:
if (BufferPusher.CanPushBuffer())
{
#if defined(EGFX_PERFORMANCE_LOG)
MeasureTimestamp = micros();
#endif
BufferPusher.StartPushBuffer(FrameBuffer.GetFrameBuffer());
if (FrameBuffer.Flip())
{
// Multi-buffering, ready for next frame drawing.
State = StateEnum::FinalizeFrame;
#if defined(EGFX_PERFORMANCE_LOG)
// Use last measured parallel push duration.
FrameTiming.PushDuration = BufferPusher.GetPushDuration();
#endif
}
else
{
// Wait for single-buffer push to complete.
State = StateEnum::WaitingForPush;
}
#if defined(EGFX_PERFORMANCE_LOG)
MeasureTimestamp = micros();
#endif
}
break;
case StateEnum::WaitingForPush:
if (BufferPusher.CanPushBuffer())
{
// Ready for next frame drawing.
State = StateEnum::FinalizeFrame;
#if defined(EGFX_PERFORMANCE_LOG)
FrameTiming.PushDuration = micros() - MeasureTimestamp;
#endif
}
break;
case StateEnum::FinalizeFrame:
State = StateEnum::Clear;
#if defined(EGFX_PERFORMANCE_LOG)
// Copy the timings for external read at any time.
memcpy(&FrameTimingCopy, &FrameTiming, sizeof(FrameTiming));
// Prepare for next frame measurement.
MeasureTimestamp = micros();
#else
// Copy the timings for external read at any time.
memcpy(&FrameTimingCopy, &FrameTiming, sizeof(DisplayTimingsStruct));
#endif
break;
default:
TS::Task::disable();
return false;
break;
}
return true;
}
private:
bool Sync(const uint32_t timestamp)
{
const uint32_t syncElapsed = timestamp - SyncReference;
bool synced = false;
switch (SyncType)
{
case DisplaySyncType::NoSync:
synced = true;
SyncReference = timestamp;
break;
case DisplaySyncType::Vrr:
if (syncElapsed >= FrameTiming.TargetDuration)
{
synced = true;
// VRR aligns to the next frame time.
SyncReference = timestamp;
}
break;
case DisplaySyncType::VSync:
if (syncElapsed >= FrameTiming.TargetDuration)
{
synced = true;
// VSync aligns to the next frame period multiple.
const uint32_t framesElapsed = syncElapsed / FrameTiming.TargetDuration;
SyncReference += FrameTiming.TargetDuration * framesElapsed;
}
break;
default:
break;
}
if (synced)
{
FrameTiming.FrameDuration = syncElapsed;
TS::Task::delay(0);
}
else
{
if (syncElapsed <= FrameTiming.TargetDuration)
{
const uint32_t sleepDuration = IntegerSignal::MaxValue<uint32_t>(1, (FrameTiming.TargetDuration - syncElapsed) / 1000) - 1;
TS::Task::delay(sleepDuration);
}
else
{
TS::Task::delay(0);
}
}
return synced;
}
};
}
#endif