Skip to content

Web (Factory.rive): embedded image assets appear to re-upload to the GPU on every artboard draw #652

Description

@SynSzakala

Environment

rive 0.14.11
rive_native 0.1.11
Flutter 3.44.7
Build flutter build web --wasm --profile (dart2wasm, skwasm renderer)
Browser Chrome 151, macOS
Factory Factory.rive

Summary

On web, an artboard that contains embedded raster assets appears to re-upload those
rasters to the GPU on every draw pass, rather than uploading once per render
texture and reusing the resident texture. The uploads are synchronous on the renderer
main thread, so each one costs a dropped frame.

Evidence

Chrome performance trace with disabled-by-default-v8.cpu_profiler enabled. All CPU
samples inside the affected main-thread slices resolve to a single stack:

RiveNativeRenderBox.paintTexture
  ArtboardWidgetPainter.paint
    BasicArtboardPainter.paint
      WebRiveArtboard.drawInternal
        Module._artboardDrawInternal
          upload_image
            texImage2D

upload_image sitting inside _artboardDrawInternal is the part that looks wrong —
it puts the upload in the draw path rather than in texture setup.

The uploads are not front-loaded. If this were a one-time residency cost they would
cluster at the start of the artboard's life. Bucketing texImage2D samples at 100 ms
against Rive paint samples over one artboard's ~915 ms of activity:

bucket (ms)   texImage2D   rive-paint
   0-100          390         523
 100-200          468         541
 200-300            0         748
 300-400            0         729
 400-500          530         578
 500-600          369         454
 600-700          507         578
 700-800          525         625
 800-900          555         653
 900-1000          93          93

first half: 1388 uploads     second half: 2049

Uploads track the paint count bucket for bucket, and the second half has more than
the first — consistent with per-draw re-upload, not per-mount.

Control case. A vector-only artboard (no embedded rasters), same build and browser,
drawing continuously for 10.5 s under sustained pointer input: 1105 Rive paint samples
and zero texImage2D samples. So the uploads are specific to artboards carrying
image assets, not to drawing in general.

Asset shape

The affected artboards are ~1425x1487 and contain a background raster at artboard size
plus 6-14 smaller object rasters (webp), 3-5 MP in total, i.e. roughly 12-19 MB once
expanded to RGBA. The images are static for the life of the artboard — nothing swaps
them at runtime, and no assetLoader replaces them (our loader only intercepts
FontAsset and returns false for everything else).

Impact

Roughly 400 ms of main-thread upload spread over ~25 slices of ~16 ms each while such
an artboard animates. Since each slice exceeds a frame budget, each one is a visible
hitch. A screen showing two RiveWidgets built from the same .riv pays it twice.

Questions

  1. Is per-draw upload of embedded image assets expected on the web/Factory.rive path,
    or is the texture cache being missed here?
  2. Is there a supported way to keep an artboard's image textures resident across draws?
  3. Does drawing into a shared texture (RivePanel / useSharedTexture) let two
    artboards from the same file share one upload, or does each artboard upload
    independently regardless?

Happy to supply the raw trace or a reduced repro if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions