From 6c7ee99e5b4a8f06160457197535934410eccdac Mon Sep 17 00:00:00 2001 From: DustinHab Date: Mon, 14 Sep 2026 23:05:51 +0200 Subject: [PATCH] Dice: replace std::mt19937 with a small generator Dice keeps a std::mt19937 as a member, 624 words of state, and seeds it from a std::seed_seq in the constructor. That seeding call alone needs a 2504 byte stack frame, and the constructor runs on the display task, which has 3200 bytes in total. A dice roll does not need a Mersenne twister. xorshift32 holds four bytes of state, the constructor frame drops from 2504 to 88 bytes, and the build comes out 832 bytes smaller. Checked on the host: 600000 d6 rolls stay within 0.7 % of even, a d99 reaches both 1 and 99, different seeds give different sequences, a zero seed does not lock the generator, and the sequence does not repeat within two million values. Rolling in the simulator gives varying results. --- src/displayapp/screens/Dice.cpp | 10 +++++----- src/displayapp/screens/Dice.h | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/displayapp/screens/Dice.cpp b/src/displayapp/screens/Dice.cpp index 302c5f3fb2..1205d5fceb 100644 --- a/src/displayapp/screens/Dice.cpp +++ b/src/displayapp/screens/Dice.cpp @@ -43,11 +43,11 @@ Dice::Dice(Controllers::MotionController& motionController, Controllers::MotorController& motorController, Controllers::Settings& settingsController) : motorController {motorController}, motionController {motionController}, settingsController {settingsController} { - std::seed_seq sseq {static_cast(xTaskGetTickCount()), - static_cast(motionController.X()), - static_cast(motionController.Y()), - static_cast(motionController.Z())}; - gen.seed(sseq); + uint32_t seed = static_cast(xTaskGetTickCount()); + seed = seed * 31 + static_cast(motionController.X()); + seed = seed * 31 + static_cast(motionController.Y()); + seed = seed * 31 + static_cast(motionController.Z()); + gen.Seed(seed); lv_obj_t* nCounterLabel = MakeLabel(&jetbrains_mono_bold_20, LV_COLOR_WHITE, diff --git a/src/displayapp/screens/Dice.h b/src/displayapp/screens/Dice.h index d12848d3cf..3f15a32500 100644 --- a/src/displayapp/screens/Dice.h +++ b/src/displayapp/screens/Dice.h @@ -29,7 +29,38 @@ namespace Pinetime { lv_task_t* refreshTask; bool enableShakeForDice = false; - std::mt19937 gen; + // std::mt19937 keeps 624 words of state and needs roughly 2.5 KB of + // stack to seed from a seed_seq, on a display task that has 3.2 KB in + // total. A dice roll does not need that much generator. + class Rng { + public: + using result_type = uint32_t; + + static constexpr result_type min() { + return 1; + } + + static constexpr result_type max() { + return UINT32_MAX; + } + + void Seed(result_type value) { + // Zero is the one state xorshift cannot leave again. + state = (value != 0) ? value : 0x9e3779b9; + } + + result_type operator()() { + state ^= state << 13; + state ^= state >> 17; + state ^= state << 5; + return state; + } + + private: + result_type state = 0x9e3779b9; + }; + + Rng gen; std::array resultColors = {LV_COLOR_YELLOW, LV_COLOR_MAGENTA, LV_COLOR_AQUA}; uint8_t currentColorIndex;