Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
- [Basics](/articles/basics.md)
- [Rendering Hello World](/essentials/render.md)
- [Migration 2.x to 3.0](/articles/migration-2x-to-3.0.md)
- [Upgrading to Renderer 1.9](/articles/renderer-1.9-upgrade.md)
- Core Concepts
- [Components](/essentials/components.md)
- [ElementNode](/essentials/elementnode.md)
Expand Down Expand Up @@ -37,7 +38,6 @@
- [Image Component](/primitives/image.md)
- [borderBox](/primitives/borderBox.md)
- [createBlurredImage](/primitives/createBlurredImage.md)
- [createTag](/primitives/createTag.md)
- [FPS Counter](/primitives/fpscounter.md)
- [LazyUp](/primitives/lazyUp.md)
- [Marquee](/primitives/marquee.md)
Expand Down
5 changes: 3 additions & 2 deletions docs/articles/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,22 @@ Config.fontSettings.color = 0xffffffff;

// Settings for SolidTV Renderer passed in for starting application
Config.rendererOptions = {
numImageWorkers: 2,
numImageWorkers: 1,
fontEngines: [SdfTextRenderer],
renderEngine: WebGlCoreRenderer,
inspector: Inspector,
// Set the resolution based on window height
deviceLogicalPixelRatio: window.innerHeight / 1080,
devicePhysicalPixelRatio: 1,
boundsMargin: 20,
boundsMargin: 200,
};
```

Here, we’re setting up a few important things:

- We’re defaulting font settings for all `<text>` nodes.
- The `rendererOptions` allow us to pass options to the SolidTV renderer, including the number of image workers, font engines, and pixel ratios.
- `boundsMargin` is the preload margin around the viewport, in logical pixels. It defaults to `200`, which is usually what you want — it loads textures ahead of a node scrolling into view.

## Routing with SolidJS Router

Expand Down
171 changes: 171 additions & 0 deletions docs/articles/renderer-1.9-upgrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Upgrading to Renderer 1.9

`@solidtv/solid` now requires `@solidtv/renderer` 1.9. The 1.8 release was a
breaking one: four features were removed or changed shape, the telemetry payload
was reworked, and three build-time defines disappeared. This page covers what an
app built on `@solidtv/solid` has to change.

Bump the peer dependency and install:

```json
"@solidtv/renderer": "^1.9.0"
```

## Render-to-texture (`rtt`) is gone

The `rtt` prop, the `RenderTexture` texture type and the `__RTT__` build define
were removed in full. There is no replacement and no compatibility shim.

```jsx
// before
<view rtt>...</view>

// after — nothing to migrate to
<view>...</view>
```

If a subtree relied on being flattened into a texture, the honest options are to
drop the effect, or to pre-render the subtree as an image asset and use that.

Two framework primitives used `rtt` internally and no longer do:

- **`FadeInOut`** flattened the subtree before its fade-out animation. Alpha is
now composited per node, so children that overlap each other can show seams
through the fade.
- **`Marquee`** rendered its scrolling text copies through a texture. They are
now drawn directly.

The `createTag` primitive, which existed only to build RTT textures, was removed
from `@solidtv/solid/primitives`.

## Bounds and viewport

### `boundsMargin` is a stage setting, not a node prop

The per-node `boundsMargin` prop is gone. The renderer setting remains, but is
now a single `number` — the `[top, right, bottom, left]` array form still parses
(the largest edge wins, with a console warning) and should be replaced.

**Its default changed from `0` to `200`.** An app that never set it now gets a
200px preload margin it did not have before. That is intended — its only job is
to load textures ahead of a node scrolling into view — but it does raise peak
texture memory.

```js
Config.rendererOptions = {
boundsMargin: 200,
};
```

### `inBounds` / `outOfBounds` events were replaced

Use `inViewport` and `outOfViewport`. `outOfViewport` fires when a node leaves
the viewport, covering both the `InViewport -> InBounds` and
`InViewport -> OutOfBounds` transitions.

```jsx
// before
<view onEvent={{ inBounds: onEnter, outOfBounds: onLeave }} />

// after
<view onEvent={{ inViewport: onEnter, outOfViewport: onLeave }} />
```

A listener on `inBounds` was firing one step earlier than viewport entry. If it
was starting a load, that job now belongs to `boundsMargin`; if it was showing
something, it was already too early and `inViewport` is what you wanted.

Both events carry a `{ previous, current }` payload of `CoreNodeRenderState`
values, and both are gated behind `__emitBoundsEvents__` — set that define to
`true` if your app listens to them.

### `renderOnlyInViewport` was removed

Its `true` behavior is now unconditional. An app that set it to `false` was
drawing everything regardless of viewport and will now draw only what is in
view.

## `autosize` is texture-only

`autosize` now means only "take the texture's intrinsic dimensions when it
loads". It no longer sizes a node to its children, and it is a no-op on `<text>`
nodes.

- A node with `src` and no children: unchanged.
- A container relying on `autosize` to measure its content: this silently stops
resizing and keeps whatever `w`/`h` it was given, frequently `0`, so the
subtree can disappear or stop clipping. Use flex (`display: 'flex'`) to size
containers, or set the size yourself.
- A `<text>` node: remove the prop.

## Texture and image loading

- **`numImageWorkers` is clamped to at most 1** and defaults to `1` (was `2`).
Passing `2` or `4` silently yields `1`. The pool measured 99.7% idle, so this
is not a regression to work around; set it to `1`, or `0` to keep image
loading on the main thread.
- **`quadBufferSize` defaults to `1048576`** (was `1310720`). Set it explicitly
if your app relied on the old default and draws an unusually large number of
quads in one frame.
- **Blob and ImageData texture sources now get a cache key.** Two textures from
the same source object share one entry, so mutating a Blob in place and
re-creating the texture no longer forces a fresh upload.

## Build defines

Three defines are gone and should be deleted — `__RTT__`, `__enableAutosize__`
and `__calculateFps__` — along with `__dirtyQuadBuffer__`, removed in 1.8.3.
Make the five surviving flags explicit; an undefined flag leaves a runtime
`typeof` check the bundler cannot fold, so the guarded branch and its imports
stay in the bundle.

```js
// vite.config.js
define: {
__DEV__: false,
__enableInspector__: false,
__emitBoundsEvents__: false, // true if you listen to viewport events
__enableCompressedTextures__: false, // true only if you ship .ktx/.pvr
__renderTextBatching__: true,
}
```

See [SolidTV Renderer](/articles/solidtv_renderer.md) for what each one guards.

## Telemetry

Only relevant if your app subscribes to `fpsUpdate`, `frameTick` or
`renderUpdate` — see the [FPS Counter](/primitives/fpscounter.md) page for the
full payload notes. In short:

- `fpsUpdateInterval` is now the single switch for frame sampling, `renderUpdate`
included, and it is honored in production builds.
- `fps` measures rendering only and is `0` over an entirely idle interval, so a
dashboard alerting on "fps below N" will fire on a quiet screen. Read
`idleTicks` alongside it.
- `capabilities` left the payload; call `renderer.getCapabilities()` once at
startup.
- `frameTick.time` is `performance.now()`, measured from page load rather than
the Unix epoch.

FPS series recorded from production builds before 1.8 are not comparable with
1.8+ numbers.

## Frame rate cap

`targetFPS` now defaults to `60` when left undefined. On TV targets an uncapped
loop draws every catch-up rAF the browser fires under GPU load, which measured
around 140fps on a 60Hz panel. Set `targetFPS: 0` to run uncapped.

## What to check after upgrading

- **Scrolling rows**: nodes appear with textures already loaded, nothing pops in
late or renders blank at the edges.
- **Every container that had `autosize`**: it still has a size, still clips, and
its children are positioned.
- **Anything that used `rtt`**: whatever replacement you settled on.
- **Viewport listeners**: entry and exit both fire, and `__emitBoundsEvents__` is
`true`.
- **Text**: letter spacing and font style measurement were fixed in 1.8, so
spaced text is measured the way it is drawn. Expect small layout shifts in text
using `letterSpacing`, and treat them as corrections.
72 changes: 42 additions & 30 deletions docs/articles/solidtv_renderer.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,70 +13,82 @@ npm install @solidtv/renderer
Or edit your package.json with:

```json
"@solidtv/renderer": "npm:@solidtv/renderer@3.2.5",
"@solidtv/renderer": "^1.9.0",
```

## Configuration (Vite Defines)

The SolidTV Renderer exposes several flags that can be configured via Vite defines in your `vite.config.ts`. These allow you to fine-tune the renderer's behavior for development, debugging, and production environments.

```js
// vite.config.ts
define: {
__DEV__: mode !== 'production',
__RTT__: true,
__renderTextBatching__: true,
__enableAutosize__: false,
__enableInspector__: mode !== 'production',
__enableCompressedTextures__: false,
__calculateFps__: mode !== 'production',
__dirtyQuadBuffer__: true,
__renderTextBatching__: true,
__emitBoundsEvents__: false,
},
```

### `__DEV__`

**Type:** `boolean` | **Default:** `undefined` (resolves `isProductionEnvironment` to `true`)

Toggles development mode. When set to `true`, it enables development-specific features, warnings, and unoptimized paths useful for debugging. In production, this should be `false` or left undefined.

### `__RTT__`
**Leaving a flag undefined is not the same as setting it to its default.** An
undefined global leaves a runtime `typeof` check that no bundler can fold, so the
guarded branch — and every module it imports — stays in the bundle even when the
flag's default is `false`. Defining all five is worth roughly 11.6 kB minified
(3.7 kB gzipped) on a WebGL build, most of it from `__enableCompressedTextures__`
dropping the PVR/KTX/ASTC parser.

**Type:** `boolean` | **Default:** `true`

Enables or disables Render To Texture (RTT). This allows offscreen rendering of complex component trees into a static texture, which can significantly improve rendering performance for static content.
> Note for esbuild users: esbuild does not do the cross-module constant
> propagation this relies on, so the constants fold but the dead branches and
> their imports remain. Rollup, and therefore Vite, does strip them.

### `__renderTextBatching__`
### `__DEV__`

**Type:** `boolean` | **Default:** `true`
**Type:** `boolean` | **Default:** `false`

Enables batching for text rendering. When enabled, the renderer batches text draw calls together, reducing overall overhead and improving text-heavy application performance. This will place Text on top of other elements. If you find Text over an element that should be on top of Text, add a zIndex to create a new layer for text.
Toggles development mode. When set to `true`, it enables development-specific features, warnings, and unoptimized paths useful for debugging. In production, this should be `false`.

### `__enableAutosize__`
### `__enableInspector__`

**Type:** `boolean` | **Default:** `false`
**Type:** `boolean` | **Default:** `!isProductionEnvironment`

Enables automatic size calculations for elements based on their content. Turning this on can have a performance cost, so it defaults to `false`. Images will still work with autosize. This shouldn't be needed as SolidTV has flex which can calculate sizes.
Enables the DOM inspector hooks in the renderer. Pair it with the `inspector`
renderer option to actually mount the inspector.

### `__enableCompressedTextures__`

**Type:** `boolean` | **Default:** `false`

Enables support for compressed texture formats. Using compressed textures can significantly reduce memory usage and improve loading times, especially on constrained devices. However most folks are not using compressed textures, so this is disabled by default. ktx, pvr are the two supported formats for compressed textures.

### `__calculateFps__`

**Type:** `boolean` | **Default:** `!isProductionEnvironment`

Calculates and exposes frames per second (FPS) metrics. By default, it is active in development mode (`__DEV__ = true`) but disabled in production to avoid unnecessary overhead.
Turning it on is only half of what a `.ktx`/`.pvr` build needs — neither
extension is in Vite's default asset list, so add `assetsInclude: ['**/*.pvr', '**/*.ktx']`
and import the asset so you get an emitted URL back.

### `__dirtyQuadBuffer__`
### `__renderTextBatching__`

**Type:** `boolean` | **Default:** `true`

An optimization technique that maintains a dirty quad buffer to reduce unnecessary recalculations during rendering updates.
Enables batching for text rendering. When enabled, the renderer batches text draw calls together, reducing overall overhead and improving text-heavy application performance. This will place Text on top of other elements. If you find Text over an element that should be on top of Text, add a zIndex to create a new layer for text.

This is a performance feature that defaults on. Define it as `true` to fold the
branch, not to turn anything off.

### `__emitBoundsEvents__`

**Type:** `boolean` | **Default:** `false`

When enabled, the renderer will emit events whenever an element's bounding box recalculates or changes. This can be useful for advanced layout tracking or debugging but may add overhead if heavily utilized.
Gates the `inViewport` / `outOfViewport` node events. Set it to `true` if your app
listens to them — see [Events](/essentials/events.md).

## Removed flags

These were removed in the 1.8 / 1.9 releases. Defining them has no effect, and
they should be deleted from your bundler config.

| Flag | Removed in | Notes |
| --------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------ |
| `__RTT__` | 1.8.0 | Render-to-texture was removed in full, along with the `rtt` prop and the `RenderTexture` texture type. No replacement. |
| `__enableAutosize__` | 1.8.0 | `autosize` is now texture-only and always on for that case. It no longer sizes a container to its children. |
| `__dirtyQuadBuffer__` | 1.8.3 | The quad buffer is now rebuilt and uploaded in full every frame; the surgical upload path it gated is gone. |
| `__calculateFps__` | 1.9.0 | `fpsUpdateInterval` is the single switch for frame telemetry (`renderUpdate` included), and it is honored in production. |
21 changes: 13 additions & 8 deletions docs/essentials/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,6 @@ In addition to the lifecycle events from SolidTV, the SolidTV Renderer offers ad
failed: (element, eventInfo) => {
console.log('fail was called');
},
inBounds: (element, eventInfo) => {
console.log('Element entered bounds');
},
outOfBounds: (element, eventInfo) => {
console.log('Element exited bounds');
},
inViewport: (element, eventInfo) => {
console.log('Element entered viewport');
},
Expand All @@ -55,11 +49,22 @@ In addition to the lifecycle events from SolidTV, the SolidTV Renderer offers ad
- **`loaded`**: Fired when the element has successfully loaded.
- **`failed`**: Fired when the element fails to load.
- **`freed`**: Fired when the element is freed for memory.
- **`inBounds`**: Fired when the element enters the bounds of the visible screen area.
- **`outOfBounds`**: Fired when the element leaves the visible screen area.
- **`inViewport`**: Fired when the element enters the viewport (the portion of the screen where content is visible).
- **`outOfViewport`**: Fired when the element leaves the viewport.

Both viewport events receive a `{ previous, current }` payload of
`CoreNodeRenderState` values.

> **Renderer 1.8 breaking change:** `inBounds` and `outOfBounds` were removed.
> They fired on the preload transition, one step earlier than viewport entry.
> Preloading textures ahead of a node scrolling on screen is now handled by the
> `boundsMargin` renderer setting, so there is nothing left for an app to do on
> that transition — move any such listener to `inViewport`.
>
> Viewport events are gated behind the `__emitBoundsEvents__` build flag, which
> defaults to `false`. See [SolidTV Renderer](/articles/solidtv_renderer.md) to
> turn it on.

These additional events provide control over element state and position within the SolidTV Renderer, allowing you to react to changes such as visibility or load state with custom logic.

## Emitting Custom Events with `emit`
Expand Down
4 changes: 4 additions & 0 deletions docs/essentials/images.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Just give any `<view>` tag a src to an image. Be sure to also give it a width an

The SolidTV renderer will scale the image to fit the width and height dimensions provided. If you don't know the size of the image you can use `autosize` attribute for the Renderer to set the image size when it loads.

Since renderer 1.8 `autosize` means only that: take the texture's intrinsic
dimensions once it loads. It does not size a container to its children (use flex
for that), and it is a no-op on `<text>` nodes.

For the best performance, it's important to keep your source images as small as possible. If you're displaying an image at `200px x 200px`, make sure the image is exactly that size or _smaller_. The latter option may lead to some quality loss, but can positively impact the overall performance of your App.

## textureOptions for images
Expand Down
Loading
Loading