Skip to content

Add S-100 Night chrome theme variant#145

Merged
philliphoff merged 2 commits into
mainfrom
philliphoff/spike-s100-chrome-themes
May 30, 2026
Merged

Add S-100 Night chrome theme variant#145
philliphoff merged 2 commits into
mainfrom
philliphoff/spike-s100-chrome-themes

Conversation

@philliphoff
Copy link
Copy Markdown
Owner

Summary

Adds an S100Night Avalonia ThemeVariant alongside the stock Light and Dark, giving the viewer chrome (panels, title bar, dialogs, buttons) a near-black, red-shifted look that preserves dark-adapted vision. The chrome theme is the user's primary axis: picking S-100 Night also flips the map portrayal palette to Night by default, while the map dropdown remains independently overridable for one-off inspection. Light and Dark are treated as system-level preferences and map to the Day portrayal palette.

This is Phase 2a of the chrome-theming initiative scoped in docs/design/s100-chrome-theme-spike.md (also included in this PR). The spike concluded with a "go" recommendation for the bounded S100Night-only scope; Day / Dusk variants are deferred.

Approach

  • New ChromeTheme enum + ChromeThemes static registry. S100Night = new ThemeVariant("S100Night", inheritVariant: ThemeVariant.Dark) so any resource key we don't override falls back through Dark — keeps the override dictionary small (~12 surface/text/border Color tokens in App.axaml).
  • IThemeService extended with Current, SetTheme(ChromeTheme), and ThemeChanged. Legacy IsDarkTheme / ToggleTheme retained for back-compat and treat S100Night as a dark variant.
  • ViewerSettings.ChromeTheme persists the selection. SettingsViewModel.SelectedChromeTheme + ChromeThemeChanged event mirror the existing SelectedPalette pattern. The SettingsViewModel constructor signature is unchanged so the existing tests keep compiling; chrome→map coupling is wired in App.axaml.cs via the event instead of via a constructor dependency.
  • Settings panel gains a Chrome Theme ComboBox with help text + ToolTip.Tip (per viewer.instructions.md), localised through new strings and a ChromeThemeNameConverter that mirrors PaletteTypeNameConverter.
  • Scale bar and compass rose updated to follow the new variant. The compass reads Application.Current.ActualThemeVariant directly because the control-level ActualThemeVariant does not always reflect custom variants for overlays sitting on top of the Mapsui control, and gets a dedicated S100Night palette so it disappears into the chrome rather than glowing.

Things worth a careful look

  • App.axaml: the custom variant must be keyed via x:Key="{x:Static services:ChromeThemes.S100Night}". Avalonia's ThemeVariantTypeConverter only accepts the built-in variant names by string and throws NotSupportedException at startup otherwise. The error doesn't show up until first launch.
  • Status colours (InfoColor, SuccessColor, WarningColor, ErrorColor) are intentionally inherited from Dark in this first cut. Retuning them for night is filed as a known follow-up in the spike doc.
  • The 8 StubThemeService test fakes across the test project were extended in-place to satisfy the larger interface. Two of them already had stateful IsDarkTheme setters and now thread SetTheme through ChromeThemes.IsDark so they remain useful as test doubles.

Spec alignment

Check each spec this PR touches and confirm the relevant skill was
consulted (.github/skills/<spec>/SKILL.md):

  • S-100 framework (s100-framework)
  • S-101 ENC (s101-enc)
  • S-102 bathymetry (s102-bathymetry)
  • S-104 water level (s104-water-level)
  • S-111 surface currents (s111-surface-currents)
  • S-124 navigational warnings (s124-nav-warnings)
  • S-129 under keel clearance (s129-ukc)
  • N/A — change is purely infrastructural (build, CI, docs, tooling)

Spec section references cited in code/docs:

The chrome theme variant motivation references S-100 Part 9 colour-profile semantics (Day / Dusk / Night portrayal palettes) for map content; the chrome itself is not spec-derived. See docs/design/s100-chrome-theme-spike.md for the full design rationale.

Tests

  • Added/updated xunit tests under tests/
  • Tests requiring real data files use SkippableFact
  • dotnet test --configuration Release passes locally

8 new tests in SettingsViewModelChromeThemeTests cover persistence round-tripping, case-insensitive parsing, fallback for invalid values, event semantics, and the ChromeThemes.GetDefaultPaletteFor / IsDark helpers. Full viewer test suite: 428/428 passing.

Documentation

  • Updated the affected project's src/<project>/README.md
  • Updated conceptual docs under docs/ if user-facing behaviour
    changed
  • New public APIs have XML doc comments

docs/design/s100-chrome-theme-spike.md is the design note that motivated and bounds this work. The Viewer README was not updated as the chrome selector is self-explanatory; happy to add a screenshot/blurb if reviewers prefer.

Dependencies

  • No new NuGet dependencies, OR versions added to
    Directory.Packages.props (not in the .csproj)
  • gh-advisory-database security check run for any new dependency

No new dependencies.

Breaking changes

IThemeService gains Current, SetTheme(ChromeTheme), and ThemeChanged. IsDarkTheme and ToggleTheme are preserved with the same semantics (S100Night counts as a dark variant for the binary signal). Internal-only interface, but any external IThemeService implementer would need to be updated. ChromeTheme is public to match the precedent set by PaletteType.

Phillip Hoff and others added 2 commits May 30, 2026 11:36
Introduces an S-100-tuned near-black chrome theme variant alongside the
stock Avalonia Light and Dark variants. The new variant is the first
phase of the chrome-theming initiative scoped in
docs/design/s100-chrome-theme-spike.md.

- ChromeTheme enum + ChromeThemes static registry that exposes the
  custom ThemeVariant (inherits from Dark for resource-key fallback).
- App.axaml ThemeDictionary x:Key="S100Night" with ~12 Color overrides
  for window / panel / dialog surfaces, text, and borders. Status
  colours intentionally inherit from Dark for now.
- IThemeService extended with Current/SetTheme/ThemeChanged; ToggleTheme
  and IsDarkTheme kept for back-compat. ThemeService routes through
  Application.Current.RequestedThemeVariant.
- ViewerSettings.ChromeTheme persists the user's selection.
- SettingsViewModel.SelectedChromeTheme + ChromeThemeChanged event.
  Ctor signature unchanged so existing tests keep compiling.
- App.axaml.cs restores chrome on startup and wires the chrome→map
  palette coupling: picking a chrome variant resets SelectedPalette to
  its default per ChromeThemes.GetDefaultPaletteFor (Light/Dark→Day,
  S100Night→Night). Users can then override the map palette manually.
- MainViewModel subscribes to ThemeChanged so settings-driven changes
  keep IsDarkTheme in sync (not just the title-bar toggle).
- CompassRoseView generalises its inline ThemeVariant.Dark check so
  S100Night does not pale-out on near-black backgrounds.
- SettingsView.axaml gains a Chrome Theme ComboBox with help text and
  ToolTip.Tip; new ChromeThemeNameConverter mirrors the existing
  PaletteTypeNameConverter pattern.
- Strings.resx/Strings.cs additions for the new selector + labels.
- 8 new tests in SettingsViewModelChromeThemeTests cover persistence,
  parsing, event semantics, and the ChromeThemes helper.

All 428 viewer tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…0-chrome-themes

# Conflicts:
#	src/EncDotNet.S100.Viewer/Services/IThemeService.cs
#	src/EncDotNet.S100.Viewer/Services/ThemeService.cs
@github-actions
Copy link
Copy Markdown
Contributor

Performance Gate

PASSED — no regressions.

Threshold: 10.0%, MAD multiplier (k): 3.0, retry-zone mult: 2.0×

Scenario summary

Scenario Status Δ median (%) z (Δ/MAD) Base median (ms) Samples (b/c)
exchange-set-open ✅ pass -6.9 -0.99 0.53 20/20
s101-portray-cold ✅ pass -0.3 -0.09 407.86 20/20
s101-portray-warm ✅ pass -0.7 -0.25 247.87 20/20
s101-render-warm ✅ pass +1.0 +0.58 238.90 20/20
s102-coverage ✅ pass -0.2 -0.15 1.03 20/20
s102-coverage-open ✅ pass +3.7 +0.84 1.76 20/20
s102-coverage-render-large ✅ pass -0.5 -0.41 114.11 20/20
s124-vector ✅ pass -6.2 -0.66 0.37 20/20
s201-vector ✅ pass -0.5 -0.06 0.38 20/20

exchange-set-open

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 0.53 0.50
Baseline MAD (ms) 0.04
Δ median -6.9%
z (Δ/MAD) -0.99

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.asset.read 10.20 10.06 -1.3% ▫️
s100.exchangeset.parse 37.56 37.58 +0.0% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.asset.read.duration 17.14 17.33 +1.1% ▫️

s101-portray-cold

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 407.86 406.74
Baseline MAD (ms) 12.61
Δ median -0.3%
z (Δ/MAD) -0.09

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.lua.execute 7440.83 7817.88 +5.1%
s100.lua.rule.invoke 6525.50 6886.27 +5.5%
s100.pipeline.vector.process 7645.06 8045.25 +5.2%
s100.pipeline.vector.stage.assemble 0.21 0.21 -1.4% ▫️
s100.pipeline.vector.stage.feature_xml 146.93 159.76 +8.7%
s100.pipeline.vector.stage.lua 7442.77 7819.83 +5.1%
s100.pipeline.vector.stage.rule_select 6.59 16.93 +157.0%
s100.pipeline.vector.stage.sort 13.09 13.28 +1.4% ▫️
s100.pipeline.vector.stage.viewing_groups 14.96 15.02 +0.4% ▫️
s100.pipeline.vector.stage.xslt 0.24 0.24 -2.9% ▫️
s100.render.frame 2054.84 2037.28 -0.9% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 7.00 7.00 +0.0% ▫️
s100.featurecatalogue.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.featurecatalogue.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.lua.execute.duration 2572.78 2576.06 +0.1% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 42.00 42.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 217.00 217.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 21.00 21.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.features.count 2478.00 2478.00 +0.0% ▫️
s100.lua.instructions.emitted.count 4004.00 4004.00 +0.0% ▫️
s100.lua.rule.invoke.count 7.00 7.00 +0.0% ▫️
s100.lua.rule.invoke.count 77.00 77.00 +0.0% ▫️
s100.lua.rule.invoke.duration 2207.70 2205.75 -0.1% ▫️
s100.lua.rule.invoke.duration 4.53 4.32 -4.6% ▫️
s100.lua.source.cache.hit.count 552.00 552.00 +0.0% ▫️
s100.lua.source.cache.miss.count 43.00 43.00 +0.0% ▫️
s100.pattern.cache.hit.count 210.00 210.00 +0.0% ▫️
s100.pattern.cache.miss.count 14.00 14.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 4004.00 4004.00 +0.0% ▫️
s100.pipeline.duration 2640.40 2655.74 +0.6% ▫️
s100.pipeline.features.in 217.00 217.00 +0.0% ▫️
s100.pipeline.stage.duration 0.27 0.26 -0.8% ▫️
s100.pipeline.stage.duration 52.34 54.32 +3.8% ▫️
s100.pipeline.stage.duration 2574.19 2577.51 +0.1% ▫️
s100.pipeline.stage.duration 4.72 15.12 +220.0%
s100.pipeline.stage.duration 4.97 4.85 -2.6% ▫️
s100.pipeline.stage.duration 1.07 1.03 -3.8% ▫️
s100.pipeline.stage.duration 0.86 0.83 -3.1% ▫️
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.portrayal.cache.hit.count 12.00 12.00 +0.0% ▫️
s100.portrayal.cache.hit.count 552.00 552.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 96.00 96.00 +0.0% ▫️
s100.portrayal.cache.miss.count 2.00 2.00 +0.0% ▫️
s100.portrayal.cache.miss.count 43.00 43.00 +0.0% ▫️
s100.portrayal.cache.miss.count 16.00 16.00 +0.0% ▫️
s100.render.frame.duration 758.07 761.10 +0.4% ▫️
s100.render.instructions.processed.count 4004.00 4004.00 +0.0% ▫️
s100.render.styles.applied.count 4256.00 4256.00 +0.0% ▫️
s100.symbol.cache.hit.count 364.00 364.00 +0.0% ▫️
s100.symbol.cache.miss.count 98.00 98.00 +0.0% ▫️
s100.symbol.resolve.duration 0.33 0.33 +0.4% ▫️
s100.symbol.resolve.duration 17.76 17.83 +0.4% ▫️

s101-portray-warm

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 247.87 246.17
Baseline MAD (ms) 6.77
Δ median -0.7%
z (Δ/MAD) -0.25

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.lua.execute 6041.55 5847.40 -3.2% ▫️
s100.lua.rule.invoke 5372.16 5217.35 -2.9% ▫️
s100.pipeline.vector.process 6337.29 6136.22 -3.2% ▫️
s100.pipeline.vector.stage.assemble 0.19 0.16 -12.9%
s100.pipeline.vector.stage.feature_xml 268.01 263.49 -1.7% ▫️
s100.pipeline.vector.stage.lua 6042.95 5848.74 -3.2% ▫️
s100.pipeline.vector.stage.rule_select 4.03 3.84 -4.9% ▫️
s100.pipeline.vector.stage.sort 16.77 15.26 -9.0% ▫️
s100.pipeline.vector.stage.viewing_groups 18.59 16.91 -9.1% ▫️
s100.pipeline.vector.stage.xslt 0.25 0.21 -16.1%
s100.render.frame 250.10 235.93 -5.7% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.lua.execute.duration 1693.61 1594.94 -5.8% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 42.00 42.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 217.00 217.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 21.00 21.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.features.count 2478.00 2478.00 +0.0% ▫️
s100.lua.instructions.emitted.count 4004.00 4004.00 +0.0% ▫️
s100.lua.rule.invoke.count 7.00 7.00 +0.0% ▫️
s100.lua.rule.invoke.count 77.00 77.00 +0.0% ▫️
s100.lua.rule.invoke.duration 1501.08 1446.55 -3.6% ▫️
s100.lua.rule.invoke.duration 1.98 2.13 +7.8%
s100.lua.source.cache.hit.count 595.00 595.00 +0.0% ▫️
s100.pattern.cache.hit.count 222.00 222.00 +0.0% ▫️
s100.pattern.cache.miss.count 2.00 2.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 4004.00 4004.00 +0.0% ▫️
s100.pipeline.duration 1765.44 1664.71 -5.7% ▫️
s100.pipeline.features.in 217.00 217.00 +0.0% ▫️
s100.pipeline.stage.duration 0.02 0.02 +2.7% ▫️
s100.pipeline.stage.duration 64.98 62.86 -3.3% ▫️
s100.pipeline.stage.duration 1693.93 1595.27 -5.8% ▫️
s100.pipeline.stage.duration 1.14 1.18 +3.5% ▫️
s100.pipeline.stage.duration 3.85 3.80 -1.2% ▫️
s100.pipeline.stage.duration 0.32 0.35 +11.9%
s100.pipeline.stage.duration 0.04 0.04 +6.6%
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.portrayal.cache.hit.count 2.00 2.00 +0.0% ▫️
s100.portrayal.cache.hit.count 595.00 595.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 16.00 16.00 +0.0% ▫️
s100.render.frame.duration 136.87 137.12 +0.2% ▫️
s100.render.instructions.processed.count 4004.00 4004.00 +0.0% ▫️
s100.render.styles.applied.count 4256.00 4256.00 +0.0% ▫️
s100.symbol.cache.hit.count 448.00 448.00 +0.0% ▫️
s100.symbol.cache.miss.count 14.00 14.00 +0.0% ▫️
s100.symbol.resolve.duration 0.29 0.31 +9.8%
s100.symbol.resolve.duration 2.22 2.17 -2.3% ▫️

s101-render-warm

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 238.90 241.25
Baseline MAD (ms) 4.03
Δ median +1.0%
z (Δ/MAD) +0.58

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.lua.execute 5731.62 5822.74 +1.6% ▫️
s100.lua.rule.invoke 5291.93 5407.36 +2.2% ▫️
s100.pipeline.vector.process 5873.14 5981.22 +1.8% ▫️
s100.pipeline.vector.stage.assemble 0.14 0.13 -5.4% ▫️
s100.pipeline.vector.stage.feature_xml 123.08 141.48 +14.9%
s100.pipeline.vector.stage.lua 5732.73 5823.85 +1.6% ▫️
s100.pipeline.vector.stage.rule_select 2.66 2.45 -8.2% ▫️
s100.pipeline.vector.stage.sort 10.64 9.49 -10.8%
s100.pipeline.vector.stage.viewing_groups 12.15 11.01 -9.4% ▫️
s100.pipeline.vector.stage.xslt 0.16 0.16 -0.4% ▫️
s100.render.frame 133.61 135.28 +1.2% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.lua.execute.duration 1610.70 1613.10 +0.1% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 42.00 42.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 217.00 217.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 21.00 21.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.features.count 2478.00 2478.00 +0.0% ▫️
s100.lua.instructions.emitted.count 4004.00 4004.00 +0.0% ▫️
s100.lua.rule.invoke.count 7.00 7.00 +0.0% ▫️
s100.lua.rule.invoke.count 77.00 77.00 +0.0% ▫️
s100.lua.rule.invoke.duration 1458.69 1484.15 +1.7% ▫️
s100.lua.rule.invoke.duration 1.88 1.80 -4.0% ▫️
s100.lua.source.cache.hit.count 595.00 595.00 +0.0% ▫️
s100.pattern.cache.hit.count 222.00 222.00 +0.0% ▫️
s100.pattern.cache.miss.count 2.00 2.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 4004.00 4004.00 +0.0% ▫️
s100.pipeline.duration 1643.08 1647.93 +0.3% ▫️
s100.pipeline.features.in 217.00 217.00 +0.0% ▫️
s100.pipeline.stage.duration 0.02 0.02 +1.9% ▫️
s100.pipeline.stage.duration 28.03 30.41 +8.5%
s100.pipeline.stage.duration 1610.97 1613.36 +0.1% ▫️
s100.pipeline.stage.duration 0.70 0.69 -1.4% ▫️
s100.pipeline.stage.duration 2.27 2.40 +5.7%
s100.pipeline.stage.duration 0.26 0.25 -2.0% ▫️
s100.pipeline.stage.duration 0.03 0.03 +17.2%
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.portrayal.cache.hit.count 2.00 2.00 +0.0% ▫️
s100.portrayal.cache.hit.count 595.00 595.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 16.00 16.00 +0.0% ▫️
s100.render.frame.duration 112.01 111.93 -0.1% ▫️
s100.render.instructions.processed.count 4004.00 4004.00 +0.0% ▫️
s100.render.styles.applied.count 4256.00 4256.00 +0.0% ▫️
s100.symbol.cache.hit.count 448.00 448.00 +0.0% ▫️
s100.symbol.cache.miss.count 14.00 14.00 +0.0% ▫️
s100.symbol.resolve.duration 0.18 0.19 +3.0% ▫️
s100.symbol.resolve.duration 1.17 1.24 +5.6%

s102-coverage

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 1.03 1.02
Baseline MAD (ms) 0.02
Δ median -0.2%
z (Δ/MAD) -0.15

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.coverage.process 42.40 42.07 -0.8% ▫️
s100.pipeline.coverage.stage.read 4.64 4.79 +3.2% ▫️
s100.pipeline.coverage.stage.resolve 34.71 34.09 -1.8% ▫️
s100.render.coverage.build 75.85 75.59 -0.3% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.coverage.cells 4557.00 4557.00 +0.0% ▫️
s100.hdf5.read.bytes 5208.00 5208.00 +0.0% ▫️
s100.hdf5.read.duration 19.51 19.65 +0.7% ▫️
s100.hdf5.read.duration 27.96 27.85 -0.4% ▫️
s100.hdf5.read.duration 6.61 6.68 +1.1% ▫️
s100.pipeline.duration 8.28 7.95 -4.0% ▫️
s100.pipeline.stage.duration 0.87 0.91 +4.8% ▫️
s100.pipeline.stage.duration 6.93 6.49 -6.5% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️

s102-coverage-open

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 1.76 1.82
Baseline MAD (ms) 0.08
Δ median +3.7%
z (Δ/MAD) +0.84

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.dataset.open 50.91 58.44 +14.8%
s100.hdf5.dataset.read 9.00 10.28 +14.3%
s100.hdf5.file.open 12.07 12.05 -0.2% ▫️
s100.hdf5.open 11.36 12.72 +12.0%

Metrics

Metric Baseline Candidate Delta Status
s100.hdf5.read.bytes 36456.00 36456.00 +0.0% ▫️
s100.hdf5.read.duration 3.92 4.21 +7.2%
s100.hdf5.read.duration 1.68 1.92 +14.4%
s100.hdf5.read.duration 2.54 2.52 -0.7% ▫️

s102-coverage-render-large

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 114.11 113.51
Baseline MAD (ms) 1.48
Δ median -0.5%
z (Δ/MAD) -0.41

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.coverage.process 191.59 188.11 -1.8% ▫️
s100.pipeline.coverage.stage.read 146.88 144.84 -1.4% ▫️
s100.pipeline.coverage.stage.resolve 41.64 40.11 -3.7% ▫️
s100.render.coverage.build 3982.92 3931.72 -1.3% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.coverage.cells 7000000.00 7000000.00 +0.0% ▫️
s100.hdf5.read.bytes 8000000.00 8000000.00 +0.0% ▫️
s100.hdf5.read.duration 0.08 0.10 +19.9%
s100.hdf5.read.duration 3.16 3.27 +3.4% ▫️
s100.hdf5.read.duration 0.92 1.17 +26.4%
s100.pipeline.duration 37.92 33.53 -11.6%
s100.pipeline.stage.duration 30.71 26.75 -12.9%
s100.pipeline.stage.duration 6.62 6.12 -7.6% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️

s124-vector

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 0.37 0.34
Baseline MAD (ms) 0.03
Δ median -6.2%
z (Δ/MAD) -0.66

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.vector.process 7.36 6.81 -7.4% ▫️
s100.pipeline.vector.stage.assemble 0.23 0.23 +0.4% ▫️
s100.pipeline.vector.stage.feature_xml 1.09 0.96 -12.1%
s100.pipeline.vector.stage.rule_select 0.26 0.25 -4.6% ▫️
s100.pipeline.vector.stage.sort 0.20 0.18 -9.1% ▫️
s100.pipeline.vector.stage.viewing_groups 0.46 0.39 -13.8%
s100.pipeline.vector.stage.xslt 3.63 3.25 -10.5%
s100.render.frame 0.99 0.96 -2.9% ▫️
s100.xslt.transform 1.46 1.41 -3.3% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 14.00 14.00 +0.0% ▫️
s100.pipeline.duration 52.34 53.89 +3.0% ▫️
s100.pipeline.features.in 7.00 7.00 +0.0% ▫️
s100.pipeline.stage.duration 0.95 0.97 +1.8% ▫️
s100.pipeline.stage.duration 2.94 3.04 +3.5% ▫️
s100.pipeline.stage.duration 1.00 1.05 +5.9%
s100.pipeline.stage.duration 0.05 0.05 +3.2% ▫️
s100.pipeline.stage.duration 0.07 0.07 +1.0% ▫️
s100.pipeline.stage.duration 47.06 48.37 +2.8% ▫️
s100.pipeline.stage.instructions.count 14.00 14.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 14.00 14.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 14.00 14.00 +0.0% ▫️
s100.portrayal.cache.hit.count 13.00 13.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.render.frame.duration 2.02 2.12 +5.2%
s100.render.instructions.processed.count 14.00 14.00 +0.0% ▫️
s100.render.styles.applied.count 14.00 14.00 +0.0% ▫️
s100.xslt.transform.duration 9.48 9.65 +1.8% ▫️

s201-vector

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 0.38 0.38
Baseline MAD (ms) 0.03
Δ median -0.5%
z (Δ/MAD) -0.06

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.vector.process 8.27 7.91 -4.4% ▫️
s100.pipeline.vector.stage.assemble 0.19 0.18 -5.6% ▫️
s100.pipeline.vector.stage.feature_xml 0.99 0.93 -6.6% ▫️
s100.pipeline.vector.stage.rule_select 0.33 0.31 -6.7% ▫️
s100.pipeline.vector.stage.sort 0.11 0.12 +8.2%
s100.pipeline.vector.stage.viewing_groups 0.40 0.40 +0.7% ▫️
s100.pipeline.vector.stage.xslt 3.57 3.43 -3.9% ▫️
s100.render.frame 0.89 0.86 -3.4% ▫️
s100.xslt.transform 2.40 2.30 -4.1% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 7.00 7.00 +0.0% ▫️
s100.pipeline.duration 130.23 129.45 -0.6% ▫️
s100.pipeline.features.in 21.00 21.00 +0.0% ▫️
s100.pipeline.stage.duration 0.56 0.54 -3.2% ▫️
s100.pipeline.stage.duration 6.69 7.09 +6.0%
s100.pipeline.stage.duration 0.15 0.12 -21.8%
s100.pipeline.stage.duration 0.02 0.02 +1.3% ▫️
s100.pipeline.stage.duration 0.04 0.04 -13.4%
s100.pipeline.stage.duration 122.47 121.30 -1.0% ▫️
s100.pipeline.stage.instructions.count 7.00 7.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 7.00 7.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.render.frame.duration 0.98 1.03 +5.0%
s100.render.instructions.processed.count 7.00 7.00 +0.0% ▫️
s100.render.styles.applied.count 14.00 14.00 +0.0% ▫️
s100.symbol.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.symbol.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.symbol.resolve.duration 0.01 0.01 +2.6% ▫️
s100.symbol.resolve.duration 0.70 0.73 +5.4%
s100.xslt.transform.duration 28.67 28.50 -0.6% ▫️

Generated by EncDotNet.S100.PerfReport gate command

@philliphoff philliphoff merged commit b194b5c into main May 30, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant