# `InfoReport.scene`

> Contract 2.0.0 · package sha256 `a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e` · generated from `configurator-runtime.d.ts` by `services/onsite-scripter/scripts/build-contract-docs.mjs`; edit the source, not this file.

Property of [`InfoReport`](InfoReport.md) · labs, `@experimental` · since 2.0.0 · called as `session.info().scene`

```ts
readonly scene: {
  readonly triangles: number;
  readonly drawCalls: number;
  readonly geometries: number;
  readonly textures: number;
  readonly programs: number;
};
```

What the last drawn frame cost, for checking a scene against a phone budget. `triangles` and
`drawCalls`: drawn in that frame, every pass counted (the ground, effects, and the shadow map in a frame that redraws it after a change, so that frame counts the casters twice).
`geometries`, `textures` and `programs` (compiled shader programs): held by the renderer then.
All zero before the first drawn frame. It changes when a frame is drawn, not at a commit:
after [`idle()`](Session.idle.md) it counts the frame that shows the last change (the example). A hidden page,
or a page while `el` is out of the viewport, may draw no frame ([`idle()`](Session.idle.md) then ends without
it), so it keeps the last one's counts.
Counts, not timings: frame time depends on the device, so measure that on the phone itself.
The fake draws no pixels: it counts 12 triangles and one draw call per shown mesh entry with
`bounds` ([`FakeModel.nodes`](testing.FakeModel.nodes.md)), one geometry per loaded one, and no textures or programs.
Experimental: check [`supports('info.scene')`](Session.supports.md) before reading it, and skip the check where it
answers false (a later minor may reshape the field).

## Fields

<a id="triangles"></a>
### `triangles`

```ts
readonly triangles: number;
```

<a id="drawcalls"></a>
### `drawCalls`

```ts
readonly drawCalls: number;
```

<a id="geometries"></a>
### `geometries`

```ts
readonly geometries: number;
```

<a id="textures"></a>
### `textures`

```ts
readonly textures: number;
```

<a id="programs"></a>
### `programs`

```ts
readonly programs: number;
```

## Example

Imports: values (`has`, `validate`, the `Holo*Error` classes, the pure helpers) from `@holostep-configurators/runtime-client`, types from `@holostep-configurators/runtime-client/contract`, `/math` and `/testing` names from those entries; `boot` from the runtime module the root's `loadRuntimeAtFloor` resolves to. A type your page defines is your app's: declare it there, never import it from the package.

Compiled under the contract's strict settings (`examples/declaration/19-InfoReport.scene.ts`):

```ts
import type { Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
// A budget check an author runs before hand-off (a mid-range phone's comfortable numbers).
if (session.supports('info.scene')) {
  await session.idle(); // the frame that shows the scene has been drawn
  const { triangles, drawCalls } = session.info().scene;
  if (triangles > 500_000 || drawCalls > 200) {
    console.warn(`over the phone budget: ${triangles} triangles, ${drawCalls} draw calls`);
  }
}
```

## Generic-API test (spec 3.1)

| Rule | Result |
| --- | --- |
| 1. Neutral name | pass: `scene` names no product |
| 2. A precedent | not recorded: no ledger row (coverage.json) names this member or its owner |
| 3. Two apps, or labs | labs (`@experimental` at a stable path, reached through `has()` or `supports()`): it graduates when a second app uses it |
| 4. The cheaper fix first | not recorded per member (Appendix B decides it per need) |
