@@ -15,6 +15,7 @@
#include <expo_test.h>
#include <log.h>
#include <malloc.h>
+#include <time.h>
#include <video.h>
#include <video_console.h>
@@ -44,6 +45,8 @@ void expo_test_checkenv(struct expo *exp)
test->enabled = env_get_yesno("expotest") == 1;
test->render_count = 0;
+ test->start_time_ms = get_timer(0);
+ test->last_update = get_timer(0);
}
void expo_test_update(struct expo *exp)
@@ -53,6 +56,44 @@ void expo_test_update(struct expo *exp)
test->render_count++;
}
+int expo_calc_fps(struct expo_test_mode *test)
+{
+ ulong oldest_time, newest_time;
+ int oldest_frames, newest_frames;
+ int frame_delta, time_delta;
+ int oldest_idx;
+ int fps;
+
+ /* Use most recent entry */
+ newest_time = test->fps_timestamps_ms[test->fps_index];
+ newest_frames = test->fps_frame_counts[test->fps_index];
+
+ /* Find oldest valid entry by looking backwards from current index */
+ oldest_idx = (test->fps_index + 1) % EXPO_FPS_AVG_SECONDS;
+ if (test->fps_timestamps_ms[oldest_idx] == 0) {
+ /* Array hasn't wrapped yet, use first entry */
+ oldest_idx = 0;
+ }
+
+ oldest_time = test->fps_timestamps_ms[oldest_idx];
+ oldest_frames = test->fps_frame_counts[oldest_idx];
+
+ /* Need at least two data points with different timestamps */
+ if (oldest_time >= newest_time)
+ return 0;
+
+ frame_delta = newest_frames - oldest_frames;
+ time_delta = newest_time - oldest_time;
+
+ if (!time_delta)
+ return 0;
+
+ /* Calculate FPS: frames / (time_ms / 1000) */
+ fps = (frame_delta * 1000) / time_delta;
+
+ return fps;
+}
+
int expo_test_render(struct expo *exp)
{
struct expo_test_mode *test = exp->test;
@@ -60,6 +101,7 @@ int expo_test_render(struct expo *exp)
struct udevice *dev = exp->display;
struct video_priv *vid_priv;
char buf[30];
+ ulong now;
int x, y;
int ret;
@@ -74,6 +116,16 @@ int expo_test_render(struct expo *exp)
vid_priv = dev_get_uclass_priv(dev);
cons_priv = dev_get_uclass_priv(exp->cons);
+ /* Update FPS if at least 1 second has elapsed */
+ if (get_timer(test->last_update) >= 1000) {
+ now = get_timer(test->start_time_ms);
+ test->fps_index = (test->fps_index + 1) % EXPO_FPS_AVG_SECONDS;
+ test->fps_timestamps_ms[test->fps_index] = now;
+ test->fps_frame_counts[test->fps_index] = test->render_count;
+ test->fps_last = expo_calc_fps(test);
+ test->last_update = get_timer(0);
+ }
+
/* Display frame count */
snprintf(buf, sizeof(buf), "frame %6d", test->render_count);
x = vid_priv->xsize - 18 * cons_priv->x_charsize;
@@ -81,5 +133,13 @@ int expo_test_render(struct expo *exp)
vidconsole_set_cursor_pos(exp->cons, x, y);
vidconsole_put_string(exp->cons, buf);
+ /* Display FPS on next line (only if non-zero) */
+ if (test->fps_last > 0) {
+ snprintf(buf, sizeof(buf), "fps %6d", test->fps_last);
+ y += cons_priv->y_charsize;
+ vidconsole_set_cursor_pos(exp->cons, x, y);
+ vidconsole_put_string(exp->cons, buf);
+ }
+
return 0;
}
@@ -577,6 +577,22 @@ API documentation
Future ideas
------------
+Test Mode
+---------
+
+Expo supports a test mode that can be enabled by setting the environment
+variable `expotest` to 1. When enabled, expo displays the frame count in the
+top-right corner of the display. This is useful for debugging and performance
+analysis.
+
+To enable test mode::
+
+ => setenv expotest 1
+ => bootflow menu
+
+The frame count shows the number of times `expo_render()` has been called since
+`expo_enter_mode()` was invoked. The counter resets each time expo mode is entered.
+
Some ideas for future work:
- Default menu item and a timeout
@@ -9,15 +9,30 @@
struct expo;
+/* Number of seconds to average FPS over in test mode */
+#define EXPO_FPS_AVG_SECONDS 5
+
/**
* struct expo_test_mode - Test mode information for expo
*
* @enabled: true if test mode is enabled
+ * @start_time_ms: Time when expo_enter_mode() was called (milliseconds)
* @render_count: Number of calls to expo_render() since expo_enter_mode()
+ * @fps_timestamps_ms: Timestamps for FPS calculation (milliseconds)
+ * @fps_frame_counts: Frame counts at each timestamp
+ * @fps_index: Current index in the FPS tracking arrays
+ * @fps_last: Last calculated FPS value
+ * @last_update: Time of last FPS update (milliseconds)
*/
struct expo_test_mode {
bool enabled;
+ ulong start_time_ms;
int render_count;
+ ulong fps_timestamps_ms[EXPO_FPS_AVG_SECONDS];
+ int fps_frame_counts[EXPO_FPS_AVG_SECONDS];
+ int fps_index;
+ int fps_last;
+ ulong last_update;
};
#if CONFIG_IS_ENABLED(EXPO_TEST)
@@ -62,6 +77,14 @@ void expo_test_update(struct expo *exp);
*/
int expo_test_render(struct expo *exp);
+/**
+ * expo_calc_fps() - Calculate FPS based on recent frame history
+ *
+ * @test: Test mode data containing frame history
+ * Return: Calculated FPS value, or 0 if insufficient data
+ */
+int expo_calc_fps(struct expo_test_mode *test);
+
#else
static inline int expo_test_init(struct expo *exp)
@@ -86,6 +109,11 @@ static inline int expo_test_render(struct expo *exp)
return 0;
}
+static inline int expo_calc_fps(struct expo_test_mode *test)
+{
+ return 0;
+}
+
#endif /* EXPO_TEST */
#endif /* __EXPO_TEST_H */
@@ -1163,3 +1163,60 @@ static int expo_test_mode(struct unit_test_state *uts)
return 0;
}
BOOTSTD_TEST(expo_test_mode, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);
+
+static int expo_test_calc_fps(struct unit_test_state *uts)
+{
+ struct expo_test_mode test;
+ int fps;
+
+ memset(&test, 0, sizeof(test));
+
+ /* No data - should return 0 */
+ fps = expo_calc_fps(&test);
+ ut_asserteq(0, fps);
+
+ /* Single data point - should return 0 */
+ test.fps_index = 0;
+ test.fps_timestamps_ms[0] = 0;
+ test.fps_frame_counts[0] = 0;
+ fps = expo_calc_fps(&test);
+ ut_asserteq(0, fps);
+
+ /* Two data points: 100 frames in 1000ms = 100 FPS */
+ test.fps_index = 1;
+ test.fps_timestamps_ms[0] = 0;
+ test.fps_frame_counts[0] = 0;
+ test.fps_timestamps_ms[1] = 1000;
+ test.fps_frame_counts[1] = 100;
+ fps = expo_calc_fps(&test);
+ ut_asserteq(100, fps);
+
+ /* Three data points spanning 2 seconds: 240 frames in 2000ms = 120 FPS */
+ test.fps_index = 2;
+ test.fps_timestamps_ms[0] = 0;
+ test.fps_frame_counts[0] = 0;
+ test.fps_timestamps_ms[1] = 1000;
+ test.fps_frame_counts[1] = 100;
+ test.fps_timestamps_ms[2] = 2000;
+ test.fps_frame_counts[2] = 240;
+ fps = expo_calc_fps(&test);
+ ut_asserteq(120, fps);
+
+ /* Test wraparound: index at 1, with data at indices 2,3,4,0,1 */
+ test.fps_index = 1;
+ test.fps_timestamps_ms[2] = 0;
+ test.fps_frame_counts[2] = 0;
+ test.fps_timestamps_ms[3] = 1000;
+ test.fps_frame_counts[3] = 60;
+ test.fps_timestamps_ms[4] = 2000;
+ test.fps_frame_counts[4] = 120;
+ test.fps_timestamps_ms[0] = 3000;
+ test.fps_frame_counts[0] = 180;
+ test.fps_timestamps_ms[1] = 4000;
+ test.fps_frame_counts[1] = 240;
+ fps = expo_calc_fps(&test);
+ ut_asserteq(60, fps); /* 240 frames in 4000ms = 60 FPS */
+
+ return 0;
+}
+BOOTSTD_TEST(expo_test_calc_fps, 0);