lib/framecounter: Use std::chrono

Bug: b/304282368
Change-Id: I640e9721fd84427c4be92ffb87d8059e6a71b2bf
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/kudzu/+/193351
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Armando Montanez <amontanez@google.com>
Commit-Queue: Anthony DiGirolamo <tonymd@google.com>
diff --git a/applications/32blit_demo/main.cc b/applications/32blit_demo/main.cc
index 2243f5b..3502489 100644
--- a/applications/32blit_demo/main.cc
+++ b/applications/32blit_demo/main.cc
@@ -11,6 +11,7 @@
 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 // License for the specific language governing permissions and limitations under
 // the License.
+#include <chrono>
 #include <cstdint>
 
 #define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG
@@ -56,27 +57,27 @@
   p.generated = true;
 };
 
-void rain(blit::Surface screen, uint32_t time_ms, blit::Rect floor_position) {
+void rain(blit::Surface screen,
+          pw::chrono::SystemClock::duration elapsed_time,
+          blit::Rect floor_position) {
   static test_particle s[300];
   static int generate_index = 0;
-  static uint32_t last_time_ms = time_ms;
 
-  int elapsed_ms = time_ms - last_time_ms;
-  float td = (elapsed_ms) / 1000.0f;
+  // Convert to fractional elapsed seconds.
+  auto const elapsed_seconds =
+      std::chrono::duration_cast<std::chrono::duration<float>>(elapsed_time);
 
   rain_generate(s[generate_index++], screen);
   if (generate_index >= 300)
     generate_index = 0;
 
-  float w = sinf(time_ms / 1000.0f) * 0.05f;
-
   blit::Vec2 gvec = blit::Vec2(0, 9.8 * 5);
-  blit::Vec2 gravity = gvec * td;
+  blit::Vec2 gravity = gvec * elapsed_seconds.count();
 
   for (auto& p : s) {
     if (p.generated) {
       p.vel += gravity;
-      p.pos += p.vel * td;
+      p.pos += p.vel * elapsed_seconds.count();
 
       int floor = -3;
       if (p.pos.x > floor_position.x &&
@@ -106,8 +107,6 @@
       screen.pixel(p.pos + blit::Point(0, screen.bounds.h + 2));
     }
   }
-
-  last_time_ms = time_ms;
 };
 
 void MainTask(void*) {
@@ -130,8 +129,6 @@
 
   display.ReleaseFramebuffer(std::move(framebuffer));
 
-  uint32_t delta_screen_draw = 0;
-
   // The display loop.
   while (1) {
     frame_counter.StartFrame();
@@ -152,11 +149,11 @@
         blit::Point((screen.bounds.w / 2) - (text_size.w / 2),
                     (screen.bounds.h * .75) - (text_size.h / 2)),
         text_size);
-    rain(screen, frame_counter.start - delta_screen_draw, text_rect);
+
+    rain(screen, frame_counter.LastFrameDuration(), text_rect);
     screen.pen = blit::Pen(0xFF, 0xFF, 0xFF);
     screen.text(
         text, blit::minimal_font, text_rect, true, blit::TextAlign::top_left);
-    delta_screen_draw = pw::spin_delay::Millis() - frame_counter.start;
 
     // Update timers
     frame_counter.EndDraw();
@@ -165,7 +162,7 @@
     frame_counter.EndFlush();
 
     // Every second make a log message.
-    frame_counter.EndFrame();
+    frame_counter.LogTiming();
   }
 }
 
diff --git a/applications/badge/main.cc b/applications/badge/main.cc
index ed48f08..b94bfc1 100644
--- a/applications/badge/main.cc
+++ b/applications/badge/main.cc
@@ -363,7 +363,7 @@
     frame_counter.EndFlush();
 
     // Every second make a log message.
-    frame_counter.EndFrame();
+    frame_counter.LogTiming();
 
     if (pw::spin_delay::Millis() > frame_start_millis + 10000) {
       Common::EndOfFrameCallback();
diff --git a/applications/snake/main.cc b/applications/snake/main.cc
index 47197f6..909fe32 100644
--- a/applications/snake/main.cc
+++ b/applications/snake/main.cc
@@ -111,7 +111,7 @@
     display.ReleaseFramebuffer(std::move(framebuffer));
     frame_counter.EndFlush();
 
-    frame_counter.EndFrame();
+    frame_counter.LogTiming();
 
     pw::spin_delay::WaitMillis(kWaitMillis);
 
diff --git a/applications/terminal_display/main.cc b/applications/terminal_display/main.cc
index d5e3c8c..bda7cb2 100644
--- a/applications/terminal_display/main.cc
+++ b/applications/terminal_display/main.cc
@@ -424,7 +424,7 @@
     frame_counter.EndFlush();
 
     // Every second make a log message.
-    frame_counter.EndFrame();
+    frame_counter.LogTiming();
   }
 }
 
diff --git a/lib/framecounter/BUILD.gn b/lib/framecounter/BUILD.gn
index cc952fc..e10083b 100644
--- a/lib/framecounter/BUILD.gn
+++ b/lib/framecounter/BUILD.gn
@@ -25,8 +25,8 @@
   public = [ "public/libkudzu/framecounter.h" ]
   public_deps = [ "$dir_pw_ring_buffer" ]
   deps = [
+    "$dir_pw_chrono:system_clock",
     "$dir_pw_log",
-    "$dir_pw_spin_delay",
   ]
   sources = [ "framecounter.cc" ]
 }
diff --git a/lib/framecounter/framecounter.cc b/lib/framecounter/framecounter.cc
index 21667f8..2c2f2b9 100644
--- a/lib/framecounter/framecounter.cc
+++ b/lib/framecounter/framecounter.cc
@@ -1,50 +1,77 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
 #include "libkudzu/framecounter.h"
 
+#include <chrono>
+
 #define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG
 #define PW_LOG_MODULE_NAME "FrameCounter"
 
 #include <cstdint>
 #include <cstring>
 
+#include "pw_chrono/system_clock.h"
 #include "pw_log/log.h"
 #include "pw_ring_buffer/prefixed_entry_ring_buffer.h"
-#include "pw_spin_delay/delay.h"
+
+using namespace std::chrono_literals;
 
 namespace kudzu {
 
 FrameCounter::FrameCounter() {
-  frame_start_millis = pw::spin_delay::Millis();
-  frames = 0;
+  second_counter_start = pw::chrono::SystemClock::now();
+  frame_count = 0;
   frames_per_second = 0;
   draw_times.SetBuffer(draw_buffer);
   flush_times.SetBuffer(flush_buffer);
 }
 
-// TODO(b/304282368): Switch to pw_chrono and delete pw_spin_delay
-void FrameCounter::StartFrame() { start = pw::spin_delay::Millis(); }
+void FrameCounter::StartFrame() {
+  frame_start = pw::chrono::SystemClock::now();
+}
 
 void FrameCounter::EndDraw() {
-  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;
+  draw_end = pw::chrono::SystemClock::now();
+  auto draw_duration = draw_end - frame_start;
+  uint32_t elapsed_millis =
+      std::chrono::round<std::chrono::microseconds>(draw_duration).count();
+  draw_times.PushBack(
+      pw::as_bytes(pw::span{std::addressof(elapsed_millis), 1}));
 }
 
 void FrameCounter::EndFlush() {
-  uint32_t time = pw::spin_delay::Millis() - start;
-  flush_times.PushBack(pw::as_bytes(pw::span{std::addressof(time), 1}));
+  frame_end = pw::chrono::SystemClock::now();
+  last_frame_duration = frame_end - frame_start;
+  auto flush_duration = frame_end - draw_end;
+  frame_count++;
+  uint32_t elapsed_millis =
+      std::chrono::round<std::chrono::microseconds>(flush_duration).count();
+  flush_times.PushBack(
+      pw::as_bytes(pw::span{std::addressof(elapsed_millis), 1}));
 }
 
-void FrameCounter::EndFrame() {
-  frames++;
-  if (pw::spin_delay::Millis() > frame_start_millis + 1000) {
-    frames_per_second = frames;
-    frames = 0;
-    PW_LOG_INFO("FPS:%d, Draw:%dms, Flush:%dms",
+void FrameCounter::LogTiming() {
+  if (frame_end - second_counter_start >
+      pw::chrono::SystemClock::for_at_least(1000ms)) {
+    frames_per_second = frame_count;
+    frame_count = 0;
+    PW_LOG_INFO("FPS:%d, Draw:%dus, Flush:%dus",
                 frames_per_second,
                 (int)CalcAverageUint32Value(draw_times),
                 (int)CalcAverageUint32Value(flush_times));
-    frame_start_millis = pw::spin_delay::Millis();
+    second_counter_start = pw::chrono::SystemClock::now();
   }
 }
 
diff --git a/lib/framecounter/public/libkudzu/framecounter.h b/lib/framecounter/public/libkudzu/framecounter.h
index 9736607..eed40a4 100644
--- a/lib/framecounter/public/libkudzu/framecounter.h
+++ b/lib/framecounter/public/libkudzu/framecounter.h
@@ -1,7 +1,22 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
 #pragma once
 
 #include <stdint.h>
 
+#include "pw_chrono/system_clock.h"
 #include "pw_ring_buffer/prefixed_entry_ring_buffer.h"
 
 namespace kudzu {
@@ -13,12 +28,25 @@
   void StartFrame();
   void EndDraw();
   void EndFlush();
-  void EndFrame();
+  void LogTiming();
+  inline pw::chrono::SystemClock::duration LastFrameDuration() {
+    return last_frame_duration;
+  }
+  inline std::chrono::milliseconds LastFrameMilliseconds() {
+    return std::chrono::round<std::chrono::milliseconds>(last_frame_duration);
+  }
 
-  uint32_t frame_start_millis;
-  uint32_t frames;
-  uint32_t start;
-  float delta_time;
+ private:
+  pw::chrono::SystemClock::time_point second_counter_start;
+  // Start of a single frame / start of the draw phase.
+  pw::chrono::SystemClock::time_point frame_start;
+  // End of the draw phase / start of the flush phase.
+  pw::chrono::SystemClock::time_point draw_end;
+  // End of the flush phase and end of the frame.
+  pw::chrono::SystemClock::time_point frame_end;
+  // Duration of frame_start to frame_end.
+  pw::chrono::SystemClock::duration last_frame_duration;
+  uint32_t frame_count;
   int frames_per_second;
   std::byte draw_buffer[30 * sizeof(uint32_t)];
   std::byte flush_buffer[30 * sizeof(uint32_t)];