terminal_demo: Add Draw metrics to upper-left corner.
Drawing the FPS, draw time, and flush time metrics to the
top left corner of the display. The text is:
FPS: %d, Draw:%dms, Flush:%dms
Flush time is the time the display/driver takes to transport the
framebuffer over the bus (currently SPI) to the display controller.
Change-Id: I6e2a967ddb764aa117c9a112791fbeb6ad17d41f
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/experimental/+/125830
Commit-Queue: Chris Mumford <cmumford@google.com>
Reviewed-by: Anthony DiGirolamo <tonymd@google.com>
diff --git a/applications/terminal_display/BUILD.gn b/applications/terminal_display/BUILD.gn
index fcd69da..718be6d 100644
--- a/applications/terminal_display/BUILD.gn
+++ b/applications/terminal_display/BUILD.gn
@@ -57,6 +57,7 @@
"$dir_pw_framebuffer",
"$dir_pw_log",
"$dir_pw_random",
+ "$dir_pw_ring_buffer",
"$dir_pw_string",
"$dir_pw_sys_io",
"$dir_pw_touchscreen",
diff --git a/applications/terminal_display/main.cc b/applications/terminal_display/main.cc
index c03b24f..cf2a178 100644
--- a/applications/terminal_display/main.cc
+++ b/applications/terminal_display/main.cc
@@ -13,6 +13,7 @@
// the License.
#include <array>
#include <cstdint>
+#include <cwchar>
#include <forward_list>
#include <memory>
#include <string_view>
@@ -22,6 +23,7 @@
#include "ansi.h"
#include "app_common/common.h"
+#include "pw_assert/assert.h"
#include "pw_assert/check.h"
#include "pw_board_led/led.h"
#include "pw_color/color.h"
@@ -34,6 +36,7 @@
#include "pw_draw/pigweed_farm.h"
#include "pw_framebuffer/rgb565.h"
#include "pw_log/log.h"
+#include "pw_ring_buffer/prefixed_entry_ring_buffer.h"
#include "pw_spin_delay/delay.h"
#include "pw_string/string_builder.h"
#include "pw_sys_io/sys_io.h"
@@ -52,6 +55,7 @@
using pw::display::Display;
using pw::draw::FontSet;
using pw::framebuffer::FramebufferRgb565;
+using pw::ring_buffer::PrefixedEntryRingBuffer;
// TODO(cmumford): move this code into a pre_init section (i.e. boot.cc) which
// is part of the target. Not all targets currently have this.
@@ -297,6 +301,20 @@
return 76;
}
+void DrawFPS(Vector2<int> tl,
+ FramebufferRgb565& framebuffer,
+ std::wstring_view fps_msg) {
+ if (fps_msg.empty())
+ return;
+
+ DrawString(fps_msg,
+ tl,
+ colors_pico8_rgb565[COLOR_PEACH],
+ kBlack,
+ pw::draw::font6x8,
+ framebuffer);
+}
+
// Draw the pigweed text banner.
// Returns the bottom Y coordinate of the bottommost pixel set.
int DrawPigweedBanner(Vector2<int> tl, FramebufferRgb565& framebuffer) {
@@ -367,7 +385,7 @@
// Draw the application header section which is mostly static text/graphics.
// Return the height (in pixels) of the header.
-int DrawHeader(FramebufferRgb565& framebuffer) {
+int DrawHeader(FramebufferRgb565& framebuffer, std::wstring_view fps_msg) {
DrawButton(
g_button, /*bg_color=*/colors_pico8_rgb565[COLOR_BLUE], framebuffer);
Vector2<int> tl = {0, 0};
@@ -377,6 +395,8 @@
constexpr int kFontSheetMargin = 4;
tl.y += kFontSheetMargin;
+ DrawFPS({1, 2}, framebuffer, fps_msg);
+
return DrawFontSheets(tl, framebuffer);
}
@@ -405,9 +425,9 @@
}
}
-void DrawFrame(FramebufferRgb565& framebuffer) {
+void DrawFrame(FramebufferRgb565& framebuffer, std::wstring_view fps_msg) {
constexpr int kHeaderMargin = 4;
- int header_bottom = DrawHeader(framebuffer);
+ int header_bottom = DrawHeader(framebuffer, fps_msg);
DrawLogTextBuffer(
header_bottom + kHeaderMargin, pw::draw::font6x8, framebuffer);
}
@@ -420,6 +440,25 @@
PW_LOG_DEBUG("Debug output");
}
+// Given a ring buffer full of uint32_t values, return the average value
+// or zero if empty.
+uint32_t CalcAverageUint32Value(PrefixedEntryRingBuffer& ring_buffer) {
+ if (!ring_buffer.EntryCount())
+ return 0;
+ uint64_t sum = 0;
+ uint32_t count = 0;
+ pw::ring_buffer::PrefixedEntryRingBufferMulti::iterator it =
+ ring_buffer.begin();
+ for (; it != ring_buffer.end(); ++it) {
+ PW_ASSERT(it->buffer.size() == sizeof(uint32_t));
+ uint32_t val;
+ std::memcpy(&val, it->buffer.data(), sizeof(val));
+ sum += val;
+ count++;
+ }
+ return sum / count;
+}
+
} // namespace
void MainTask(void* pvParameters) {
@@ -427,6 +466,15 @@
uint32_t frame_start_millis = pw::spin_delay::Millis();
uint32_t frames = 0;
int frames_per_second = 0;
+ std::array<wchar_t, 40> fps_buffer = {0};
+ std::wstring_view fps_view(fps_buffer.data(), 0);
+ std::byte draw_buffer[30 * sizeof(uint32_t)];
+ std::byte flush_buffer[30 * sizeof(uint32_t)];
+ PrefixedEntryRingBuffer draw_times;
+ PrefixedEntryRingBuffer flush_times;
+
+ draw_times.SetBuffer(draw_buffer);
+ flush_times.SetBuffer(flush_buffer);
pw::log_basic::SetOutput(LogCallback);
@@ -444,7 +492,7 @@
pw::coordinates::Vec3Int last_frame_touch_state(0, 0, 0);
- DrawFrame(framebuffer);
+ DrawFrame(framebuffer, fps_view);
// Push the frame buffer to the screen.
display.ReleaseFramebuffer(std::move(framebuffer));
@@ -472,21 +520,34 @@
last_frame_touch_state.y = point.y;
last_frame_touch_state.z = point.z;
+ uint32_t start = pw::spin_delay::Millis();
framebuffer = display.GetFramebuffer();
pw::draw::Fill(&framebuffer, kBlack);
- DrawFrame(framebuffer);
+ DrawFrame(framebuffer, fps_view);
+ uint32_t end = pw::spin_delay::Millis();
+ uint32_t time = end - start;
+ draw_times.PushBack(pw::as_bytes(pw::span{std::addressof(time), 1}));
+ start = end;
display.ReleaseFramebuffer(std::move(framebuffer));
+ time = pw::spin_delay::Millis() - start;
+ flush_times.PushBack(pw::as_bytes(pw::span{std::addressof(time), 1}));
// Every second make a log message.
frames++;
if (pw::spin_delay::Millis() > frame_start_millis + 1000) {
+ frames_per_second = frames;
+ frames = 0;
PW_LOG_INFO("Time: %lu - FPS: %d",
static_cast<unsigned long>(pw::spin_delay::Millis()),
frames_per_second);
-
- frames_per_second = frames;
- frames = 0;
+ int len = std::swprintf(fps_buffer.data(),
+ fps_buffer.size(),
+ L"FPS:%d, Draw:%dms, Flush:%dms",
+ frames_per_second,
+ CalcAverageUint32Value(draw_times),
+ CalcAverageUint32Value(flush_times));
+ fps_view = std::wstring_view(fps_buffer.data(), len);
frame_start_millis = pw::spin_delay::Millis();
}