ES
games/satisfying-bounce / src / Lib / Shorts / Quality.php · commit 8bdd6e4
<?phpnamespace JumPHP\Shorts;use JumPHP\Kernel\Platform;use JumPHP\Renderer\Renderer;/** * How much a scene may draw. A film (video:make, JUMPHP_VIDEO_OUT) is always at full quality, nothing here touches it. A game played * live is `live`: shapes come from Sprites, and on a phone the picture is drawn at most 3x the view, then, if the frame rate falls * under about 45 fps for a second, one step lighter (level 2 -> 1: drawn at 2x, fewer sparks and halos; 1 -> 0: no halos, a third of * the sparks). It only ever goes down, one step at a time with a pause between them, so it never flickers between two looks. * JUMPHP_QUALITY=0|1|2 (env or page) fixes the level and stops the adaptation. */final class Quality{    /** playing live: use the fast shapes (false while a film is rendered, and until begin() is called) */    public static bool $live = false;    /** JUMPHP_PROBE: the scenes time their update / render / audio (Quality::$probe) */    public static bool $probing = false;    /** 2 full, 1 lighter, 0 lightest */    public static int $level = 2;    private static bool $fixed = false;    private static bool $started = false;    private static float $last = 0.0;    private static float $ema = 0.0167;    private static int $slow = 0;    private static int $pause = 0;    /** Call in the scene's enter(): $recording = Recorder::isRecording(). */    public static function begin(Renderer $r, bool $recording): void    {        self::$live = !$recording;        self::$probing = (bool)Platform::setting('JUMPHP_PROBE');        self::$last = 0.0;        self::$slow = 0;        if (!self::$live) {            self::$level = 2;            return;        }        // a new scene starts one step better than the last one ended (a heavy scene must not leave the light ones dull), and its        // first moments (textures and voices being made) are not measured        if (!self::$fixed && self::$level < 2) {            self::$level++;        }        self::$pause = 120;        self::$ema = 0.0167;        $forced = Platform::setting('JUMPHP_QUALITY');        if ($forced !== null && $forced !== '') {            self::$level = max(0, min(2, (int)$forced));            self::$fixed = true;        }        if (!self::$started) {            self::$started = true;            if (Platform::isMobile() && !self::$fixed && $r->hd() > 3) {                $r->setHd(3);            }            if (self::$fixed && self::$level < 2) {                $r->setHd(2);            }        }    }    /** Call once per frame (the scene's update()): watches the real time between two calls. */    public static function frame(Renderer $r): void    {        if (!self::$live || self::$fixed) {            return;        }        $now = microtime(true);        $dt = $now - self::$last;        self::$last = $now;        if ($dt <= 0 || $dt > 0.25 || $dt === $now) {          // the first frame, a pause, a scene change: not a measure            return;        }        self::$ema += 0.08 * ($dt - self::$ema);        if (self::$pause > 0) {            self::$pause--;            return;        }        self::$slow = self::$ema > 0.0225 ? self::$slow + 1 : max(0, self::$slow - 2);        if (self::$slow >= 60 && self::$level > 0) {            self::$level--;            self::$slow = 0;            self::$pause = 180;            self::$ema = 0.0167;            if (self::$level === 1 && $r->hd() > 2) {                $r->setHd(2);            }        }    }    /** How many sparks to make instead of $n. */    public static function count(int $n): int    {        if (!self::$live || self::$level >= 2) {            return $n;        }        return max(1, (int)ceil($n * (self::$level === 1 ? 0.6 : 0.3)));    }    /** Halos and trails drawn? */    public static function halos(): bool    {        return !self::$live || self::$level >= 1;    }    /** @var array<string, float> seconds spent in the parts of the frame, when JUMPHP_PROBE is set (measuring) */    public static array $probe = [];    /** Adds the time since $since to the part $name (JUMPHP_PROBE only); returns now. */    public static function lap(string $name, float $since): float    {        $now = microtime(true);        self::$probe[$name] = (self::$probe[$name] ?? 0.0) + $now - $since;        return $now;    }    /** Smoothed seconds per frame, for measuring. */    public static function frameTime(): float    {        return self::$ema;    }}