Skip to content

feat: add scene content stats to the Current Scene debug widget - #9457

Draft
dalkia wants to merge 9 commits into
devfrom
feat/current-scene-content-stats-debug
Draft

feat: add scene content stats to the Current Scene debug widget#9457
dalkia wants to merge 9 commits into
devfrom
feat/current-scene-content-stats-debug

Conversation

@dalkia

@dalkia dalkia commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Pull Request Description

What does this PR change?

Adds per-scene content statistics to the "Current scene" debug widget so creators and devs can see, at a glance, how close the scene they are standing in is to its limits. Eight new rows appear at the top of the widget. Rows with a documented scene limitation are formatted current / maxcap (pct%) and colored green (< 80%) or yellow (≥ 80%) — the docs define these as soft limits ("reported as warnings by the Creator Hub... treat them as strong recommendations"), so exceeding one warns but never shows red. Rows without a documented limit are shown as plain counts:

  • Entities — live CRDT entities (EntitiesMap.Count)
  • Triangles — summed from primitive mesh renderers + GLTF container renderers
  • Meshes (bodies) — renderer count (primitive + GLTF)
  • Geometries — unique Mesh instances
  • Materials / Textures — unique instances, deduped across SDK materials and GLTF-embedded materials (textures probed from common shader properties)
  • Colliders — primitive colliders + GLTF invisible/decoded-visible colliders
  • External content — media streams not served from the content server + NFT shapes

Intentionally omitted — Content size. An earlier revision showed a "Content size" row that summed runtime memory of unique meshes + textures (Profiler.GetRuntimeMemorySizeLong) against the 15MB/parcel budget. It was removed: the deployed-size budget is validated at deploy time (catalyst) and by the SDK tooling, so it is not this client widget's responsibility — and runtime memory is a different quantity anyway (decompressed textures dwarf their deployed size, so the row showed red on deploy-compliant scenes).

Caps are hardcoded from the documented Decentraland scene limitations, scaled by parcel count n — exactly the five formulas on that page: triangles n×10000, entities n×200, bodies n×300, materials log2(n+1)×20, textures log2(n+1)×10. Geometries, colliders and external content have no cap and are shown as plain counts: geometries only had a formula in the legacy SDK6-era docs (dropped from the current page), and colliders/external content were never documented — earlier revisions used project-invented caps for all three, which misrepresented them as official budgets. All caps live in one constants block in DebugViewCurrentSceneSystem.Formatting.cs.

Agent/automation surface — get_scene_content_stats MCP tool. The embedded MCP server gains a read-only tool returning the same stats as structured JSON (values + the documented caps for the scene's parcel count + a fresh flag). It sets its own collection-demand flag and waits for the scene world to complete a counting pass (CollectionCount stamp), so it works with every stats UI closed — agents iterating on scenes can assert budgets programmatically instead of reading screenshots. Documented in docs/mcp-automation.md; covered by GetSceneContentStatsToolShould.

Creator-facing surface — Scene Metrics panel. The same stats are also shown to creators through a third sidebar button (signal-bars icon) in the scene debug menu at the top right, next to the console and debug-panel buttons. That menu only exists in local scene development mode or with --scene-console, so this reaches creators previewing their scene without exposing anything to regular players. The panel (MetricsPanelView) renders the identical eight rows; row formatting and caps live in a shared SceneContentStatsFormatter (DCL.Profiling) so both UIs cannot drift apart.

Architecture: a new scene-world system (SceneContentStatsSystem, SyncedPresentationSystemGroup) counts every 30 frames and writes into a new SceneContentStats payload on SceneRuntimeMetrics (now exposed to scene-world systems via ECSWorldInstanceSharedDependencies). Collection runs on demand: the debug widget (DebugViewCurrentSceneSystem) and the metrics panel (DebugMenuController) each set their own request flag (RequestedByDebugWidget / RequestedByMetricsPanel) only while visible — with both closed, the scene system does a single bool check per frame and nothing else. Counting is allocation-free (reused HashSets / scratch list, non-alloc GetSharedMaterials).

Test Instructions

Steps (standard run):

metaforge explorer run 9457

Expected result:
The "Current scene" debug widget shows the eight new stat rows with colored current / max (pct%) values for the scene you are standing in.

Steps (fresh account):

metaforge account create --clear
metaforge explorer run 9457

Expected result:
Same as above after finishing onboarding.

Automation (if applicable):
N/A

Prerequisites

  • Run with the debug panel available (editor play mode or a build with --debug)

Test Steps

  1. Enter any world/scene and open the debug panel
  2. Expand the "Current scene" widget
  3. Rows show "—" for up to ~1 second, then populate — capped rows as current / max (pct%) colored green/yellow, uncapped rows (geometries, colliders, external content) as plain counts
  4. Walk into a neighboring scene — values reset and repopulate for the new scene, and caps change with the scene's parcel count
  5. Collapse the widget or close the panel — values freeze (collection stops); reopen and they refresh within a second
  6. Visit a heavy scene (e.g. Genesis Plaza) and verify material/texture counts turn yellow (soft-limit warning; nothing renders red)
  7. Scene Metrics panel (local scene development / --scene-console only): click the signal-bars button in the top-right sidebar — a "SCENE METRICS" panel opens with the same eight rows and live values; close it and collection stops (unless the debug widget is also open)
  8. MCP tool (launch with --mcp): call get_scene_content_stats with all stats UIs closed — it returns the eight values + caps as JSON with fresh: true within ~1s

Additional Testing Notes

  • There is no "Content size" row — deployed-size validation belongs to deploy time / SDK tooling (see "Intentionally omitted" above)
  • GLTF stats only count containers in Finished state, so values grow while a scene streams in
  • Empty/loading scenes show "—" until the first collection pass
  • No SDK behavior changes — this is debug-panel-only

Quality Checklist

  • Changes have been tested locally
  • Documentation has been updated (if required)
  • Performance impact has been considered
  • For SDK features: Test scene is included

Code Review Reference

Please review our Branch & PR Standards before submitting. It explains the automated review flow, QA/DEV approval requirements, and what each label does — especially useful for first-time contributors.

🤖 Generated with Claude Code

Adds per-scene content statistics (entities, triangles, meshes/bodies,
geometries, materials, textures, colliders, runtime content size and
external content) to the "Current scene" debug widget, shown as
current / maxcap (pct%) colored green/yellow/red against hardcoded
caps derived from the scene's parcel count.

Counting runs in a new scene-world system (SceneContentStatsSystem)
gated by a demand flag the widget sets only while expanded, so it
costs a single bool check per frame when the debug panel is closed
or disabled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

@dalkia dalkia self-assigned this Jul 21, 2026
@dalkia dalkia added the force-build Used to trigger a build on draft PR label Jul 23, 2026
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

badge

Lint ratchet not evaluated for this run.

@github-actions

Copy link
Copy Markdown
Contributor

Tests: 23922 passed, 0 failed

dalkia and others added 8 commits July 24, 2026 09:53
Deployed-size validation belongs to deploy time (catalyst) and SDK
tooling; the runtime-memory measurement compared a different quantity
against the 15MB/parcel budget and showed red on deploy-compliant
scenes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep the five caps that exist on the official scene-limitations page
(triangles, entities, bodies, materials, textures) and show geometries,
colliders and external content as plain counts - their caps were
project-invented (geometries only existed in legacy SDK6 docs). The
docs define the limits as soft, so exceeding one now renders yellow
instead of red.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a third sidebar button to the creator-facing scene debug menu
(local scene development / --scene-console) that opens a Scene Metrics
panel showing the same content stats as the Current Scene debug widget.

Formatting and caps move to a shared SceneContentStatsFormatter in
DCL.Profiling so both consumers render identical rows, and the
collection demand flag is split per consumer so either UI can drive
the scene-world counting system independently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds get_scene_content_stats to the embedded MCP server so agents and
scripts can read the same numbers as the Current Scene debug widget and
the scene metrics panel as structured JSON, including the documented
soft-limit caps for the scene's parcel count.

The tool sets its own demand flag and waits for the scene world to
complete a counting pass (CollectionCount stamp), so it returns fresh
values even while every stats UI is closed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
get_scene_content_breakdown ranks the current scene's rendered content
by triangles, grouped by GLTF source (plus one aggregate row for
primitive meshes), reporting instances, renderers and share of the
scene total. The stats system fills the breakdown during a normal
counting pass when a one-shot flag is set, so it costs nothing unless
requested. Answers 'what should I optimize', not just 'over budget'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each breakdown entry now reports the source's unique material count and
a draw-call estimate (material slots across renderers, pre-batching),
and get_scene_content_breakdown gains a sortBy argument
(triangles/materials/drawCalls) so an agent can answer 'where do my
2,000 materials come from' directly. Primitive meshes flow through the
same grouping path instead of dedicated accumulators.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

force-build Used to trigger a build on draft PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant