Skip to content

Silhouettes consume high amounts of RAM #584

Open
@adroitwhiz

Description

@adroitwhiz

Expected Behavior

Scratch should not consume excessive RAM, in order to be usable on devices with low amounts of it.

Actual Behavior

Because of the way GPUs' pipelines work, reading data back from the GPU is slow, which could potentially bottleneck blocks like "touching sprite" and "touching color" which require reading and processing color data.

To mitigate this, scratch-render implements a software rendering pipeline, which does not require reading any data back from the GPU. This software renderer stores texture data in "silhouettes".

Unfortunately, this means that the more "silhouettes" there are, the more RAM will be used.
Uncompressed texture data takes up 4 bytes per pixel, so projects with many large costumes will consume high amounts of RAM.

One instance of this can be seen with the "Bad Apple Animation" project, as described in this issue. The reproduction steps have changed slightly-- the project no longer crashes while loading, but watching the animation quickly causes RAM usage to skyrocket. This is somewhat of a pathological case-- there are 730 costumes, each with a resolution of 1537x1153. This means 6.76 MiB of texture data per costume, for a total of nearly 5 GiB across all costumes. Other projects like Seagulls consume a few hundred MiB in my testing-- likely higher in full-screen mode or on high-DPI screens.

I can think of a few different ways to reduce the amount of RAM used by silhouettes:

  • For vector skins, don't grab the silhouette's texture data from the SVG renderer until it's actually needed. This was attempted in Implement updateSilhouette to allow updates to happen when needed #394, but due to certain places in the codebase not properly calling updateSilhouette to indicate that they needed the texture data, it caused bugs and was reverted in Revert "Merge pull request #394 from paulkaplan/defer-silhouette-upda… #398. Once Handle matrix + silhouette updates better #555 is merged, that issue will be fixed and such a change can be implemented again.
  • For other types of skins, the texture data could be read back from the GPU when needed (by rendering the skin to a framebuffer and reading that). This will be slower than passing the texture data to the silhouette as soon as the skin receives it, but the RAM reduction may be worth it.
  • Silhouettes' texture data could be automatically disposed of if unused for a certain amount of time. Tuning the heuristics used for determining when this data is discarded could quickly become a huge time sink, however.
  • Certain functions, like pick, could be updated to work on both the CPU and GPU so as to not require silhouette texture data to exist. This could, however, cause mysterious bugs due to differences between the CPU renderer and GPU renderer-- such a class of bugs already exists in the codebase and is causing quite a lot of trouble.
    • Alternatively, some functions could be rewritten to only use the GPU. While reading data back from the GPU does slow things down, functions like pick appear to only be called when a mouse click event is fired, which is likely not often enough to make the slowdown noticeable. For such functions, the performance penalty from reading GPU data may be preferred to the RAM penalty of keeping that data for the CPU rendering pipeline.

Steps to Reproduce

In Chrome:

  1. Open a project, preferably in a development build of the GUI so that source maps are present and you can see non-minified function names. Typically, projects with many costumes (e.g. animations) exhibit this RAM usage pattern. I recommend Bad Apple, or Seagulls if you don't have 5 GB of RAM to spare.
  2. If the project is an animation, make sure to watch it all the way through to ensure the silhouettes get created. If it's not an animation, poke around and try to view as many costumes as you can.
  3. Open the dev tools.
  4. Go to the Memory tab.
  5. Select "Heap snapshot".
  6. Select the JavaScript VM instance that corresponds to the Scratch GUI instance where the project was run. Note that it may only indicate that a very small amount of RAM is being used-- it is wrong.
  7. Click "Take snapshot", and be very patient. It can take a few minutes to load, and the "Building dominator tree" step in particular takes a long time.
    heapsnapsetup
  8. Observe the amount of RAM used by Silhouette objects:
    heapsnapresults2

Operating System and Browser

All

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions