Skip to content

perf(image-asset): keep encoded images encoded; decode pixels on demand - #194

Merged
triniwiz merged 1 commit into
masterfrom
perf/image-asset-lazy-pixels
Nov 8, 2026
Merged

triniwiz merged 1 commit into
masterfrom
perf/image-asset-lazy-pixels

Conversation

@triniwiz

@triniwiz triniwiz commented Nov 8, 2026

Copy link
Copy Markdown
Member

An image loaded from a file, URL or encoded bytes kept its whole decoded RGBA buffer for its lifetime, although 2D draws from the GPU copy. It now keeps Skia's lazy image over the encoded bytes:

  • Loading still decodes once, so a broken image fails at load as before, then the pixels are dropped.
  • 2D draws the lazy image: Skia decodes it to upload, then draws from the GPU copy.
  • Byte readers (WebGL and WebGPU uploads, ImageBitmap, the getters) decode for the call. Writers turn it into owned pixels first.
  • Images made from raw pixels keep their pixels as before.
  • The encoded bytes are now copied (Data::new_copy), because the lazy image decodes from them long after the caller's buffer is gone.

Measured

Android emulator (API 37, arm64), native heap and PSS in MB:

scene master this PR
8192 sprites from two sheets, after 1 min native 85.0, PSS 195.3 native 69.6, PSS 176.7
a three.js texture reloaded 121 times native 60.6, no errors

Rendering changes

Screenshots of a fixed frame, compared pixel by pixel:

  • Opaque images draw the same. A JPEG had 18 of about 660k pixels differ; an opaque PNG had none.
  • Semi-transparent images drawn scaled are now filtered premultiplied. This removes a dark halo the unpremultiplied raster drew at their edges.
  • Images with an embedded color profile are now color-managed when drawn in 2D.

Byte readers are unchanged: unpremultiplied RGBA with no color conversion.

Noticed, not changed

canvas-2d has no 2d feature, so the #[cfg(feature = "2d")] raster_image() path in drawing_images never compiles. Every drawImage(asset) goes through with_skia_image_bytes, which this PR keeps from decoding for lazy images.

Tested on an Android emulator only.

An image loaded from a file, URL or encoded bytes kept its whole decoded RGBA buffer for its
lifetime, even though 2D draws from the GPU copy. It now keeps Skia's lazy image over the
encoded bytes, as Chrome keeps images:

- loading still decodes once, so a broken image fails at load as before, then drops the pixels
- 2D draws the lazy image: Skia decodes it to upload, then draws from the GPU copy
- readers of bytes (WebGL and WebGPU uploads, ImageBitmap, getters) decode for the call
- writers turn it into owned pixels first
- images from raw pixels keep their pixels as before

The encoded bytes are now copied (Data::new_copy): the lazy image decodes from them long after
the caller's buffer is gone.

In apps/demo-leak on an Android emulator: 8192 sprites from two sheets ended at 69.6 MB of
native heap instead of 85.0 MB (PSS 176.7 vs 195.3 MB); 121 three.js texture reloads ended at
60.6 MB native.

Rendering: opaque images draw the same pixels as before. Semi-transparent images drawn scaled
are now filtered premultiplied, as Chrome draws them, which removes a dark halo the
unpremultiplied raster showed at their edges. Images with an embedded color profile are now
color-managed when drawn in 2D. Byte readers are unchanged: unpremultiplied RGBA with no color
conversion.
@coderabbitai

coderabbitai Bot commented Nov 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d65c4d87-80bd-4899-aac3-a235455c6b2d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@triniwiz
triniwiz merged commit a999569 into master Nov 8, 2026
23 of 25 checks passed
@triniwiz
triniwiz deleted the perf/image-asset-lazy-pixels branch November 8, 2026 04:13
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