feat: Add GLSL renderer and implement FPS overlay across rendering backends

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-19 19:12:39 +01:00
parent c62ea013ba
commit c8cd2cb144
9 changed files with 344 additions and 8 deletions

View File

@@ -74,6 +74,12 @@ class WolfEngine {
/// Elapsed engine lifetime in milliseconds.
int get timeAliveMs => _timeAliveMs;
/// Exponential moving average of rendered frames per second.
double _smoothedFps = 0.0;
/// Current smoothed FPS, suitable for lightweight on-screen diagnostics.
double get fps => _smoothedFps;
/// The episode index where the game session begins.
final int? startingEpisode;
@@ -203,6 +209,14 @@ class WolfEngine {
// Trust the incoming delta time natively
_timeAliveMs += delta.inMilliseconds;
if (delta.inMicroseconds > 0) {
final double instantaneousFps = 1000000.0 / delta.inMicroseconds;
if (_smoothedFps <= 0.0) {
_smoothedFps = instantaneousFps;
} else {
_smoothedFps = (_smoothedFps * 0.88) + (instantaneousFps * 0.12);
}
}
// 1. Process User Input
input.update();

View File

@@ -351,6 +351,13 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
}
}
@override
void drawFpsOverlay(WolfEngine engine) {
const int textColor = 0xFFFFFFFF;
const int bgColor = 0xFF000000;
_writeString(1, 0, ' ${fpsLabel(engine)} ', textColor, bgColor);
}
@override
void drawMenu(WolfEngine engine) {
final int bgColor = _rgbToPaletteColor(engine.menuBackgroundRgb);

View File

@@ -79,6 +79,7 @@ abstract class RendererBackend<T>
if (engine.difficulty == null) {
drawMenu(engine);
drawFpsOverlay(engine);
return finalizeFrame();
}
@@ -88,6 +89,7 @@ abstract class RendererBackend<T>
// 3. Draw 2D overlays.
drawWeapon(engine);
drawHud(engine);
drawFpsOverlay(engine);
// 4. Finalize and return the frame data (Buffer or String/List).
return finalizeFrame();
@@ -139,6 +141,14 @@ abstract class RendererBackend<T>
/// Default implementation is a no-op for backends that don't support menus.
void drawMenu(WolfEngine engine) {}
/// Draws a small FPS diagnostic in the top-left corner.
///
/// Backends can override this to render text in their native format.
void drawFpsOverlay(WolfEngine engine) {}
/// Returns a compact FPS label used by renderer-specific overlays.
String fpsLabel(WolfEngine engine) => 'FPS ${engine.fps.round()}';
/// Plots a VGA image into this backend's HUD coordinate space.
///
/// Coordinates are in the original 320x200 HUD space. Backends that support

View File

@@ -333,6 +333,18 @@ class SixelRenderer extends CliRendererBackend<String> {
drawStandardVgaHud(engine);
}
@override
void drawFpsOverlay(WolfEngine engine) {
const int panelColor = 0;
const int textColor = 15;
final String label = fpsLabel(engine);
final int textWidth = WolfMenuFont.measureTextWidth(label, 1);
final int panelWidth = (textWidth + 8).clamp(16, 96);
_fillRect320(2, 2, panelWidth, 10, panelColor);
_drawMenuText(label, 4, 3, textColor, scale: 1);
}
@override
/// Blits a VGA image into the Sixel index buffer HUD space (320x200).
void blitHudVgaImage(VgaImage image, int startX320, int startY200) {

View File

@@ -121,6 +121,19 @@ class SoftwareRenderer extends RendererBackend<FrameBuffer> {
drawStandardVgaHud(engine);
}
@override
void drawFpsOverlay(WolfEngine engine) {
const int panelX = 2;
const int panelY = 2;
const int panelW = 72;
const int panelH = 10;
final int panelColor = ColorPalette.vga32Bit[0];
final int textColor = ColorPalette.vga32Bit[15];
_fillMenuPanel(panelX, panelY, panelW, panelH, panelColor);
_drawMenuText(fpsLabel(engine), panelX + 3, panelY + 1, textColor);
}
@override
/// Blits a VGA image into the software framebuffer HUD space (320x200).
void blitHudVgaImage(VgaImage image, int startX320, int startY200) {