Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canvas2D Performance Experiments #322

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

frank-weindel
Copy link
Collaborator

@frank-weindel frank-weindel commented Jul 4, 2024

Experiments to squeeze more performance out of Canvas2D font rendering (in separate commits)

  • Remove a transparent texture upload we do before each texture load (L2 doesn’t do this at all and I don’t think it’s necessary in the Renderer right now)
  • Remove some additional options we provide when getting the WebGL context.
    • One or more of them seem to slow things down a bit and L2 doesn’t use any of them
  • Group TextRenderer updates in one big queueMicrotask (instead of one for each update)
  • Give each TextNode its own canvas and upload the canvas directly to the GPU texture. L2 does this.
  • Eliminate a call to generateMipmap() for every loaded texture. L2 doesn’t generate mipmaps.
  • Provide a way for textures to be loaded synchronously in addition to asynchronously, and load the text canvas synchronously (L2 does this)
  • Optimize font loading in the CanvasTextRenderer to use document.fonts.check() (L2 does this)
  • Use the Lightning2 TextTextureRenderer class in a more compressed manner, pretty close to how L2 uses it.
    • i.e. Do all of this one after the other when generating a text texture synchronously: Create the canvas, create the Lightning2TextTextureRenderer, calculateRenderInfo, and draw.

These changes combined don't add up to what L2 is currently producing. There is some kind of optimizations happening on the JIT side for L2 that for some reason yet to be known isn't kicking in for L3.

wouterlucas and others added 11 commits July 3, 2024 21:08
- Canvas Text Textures are now managed properly by the texture manager
- Also add willReadFrequently option to text drawing canvas element context
Do all of this one after the other when generating a text texture syncronously: Create the canvas, create the Lightning2TextTextureRenderer, caculateRenderInfo, and draw. L2 does it this way.
@elsassph
Copy link
Contributor

elsassph commented Jul 4, 2024

One concerning area of code I'm seeing is the overly complicated and dynamic TrPropSetters.

  • not everything needs to be a setter; the state is entirely internal,
  • though it's unintuitive, Object.freeze literally makes code slower (in particular in our old TV browsers).

Essentially, IMHO this is incredibly inefficient:

  set fontSize(value: CoreTextNodeWritableProps['fontSize']) {
    this.textRenderer.set.fontSize(this.trState, value);
  }

While something more straightforward is likely to be much faster:

  set fontSize(value: CoreTextNodeWritableProps['fontSize']) {
    if (this.trState.props.fontSize !== value) {
      this.trState.props.fontSize = value;
      this.textRenderer.invalidate();
    }
  }

@elsassph
Copy link
Contributor

elsassph commented Jul 4, 2024

Another consideration would be: could there be just one shared canvas for all the renderers? I'm not sure keeping the canvas and context references for the whole life of the text node is useful.

@frank-weindel
Copy link
Collaborator Author

Another consideration would be: could there be just one shared canvas for all the renderers? I'm not sure keeping the canvas and context references for the whole life of the text node is useful.

@elsassph This is how it's currently implemented. I was just trying this out because L2 does it that way (though it doesn't hold onto the references at all).

@frank-weindel
Copy link
Collaborator Author

One concerning area of code I'm seeing is the overly complicated and dynamic TrPropSetters.

The point here is to allow text renderers to decide themselves what property changes require what kind of invalidations. For instance, changing the color prop doesn't need to trigger any kind of layout or texture generation for SDF text but it does require texture generation for Canvas text (though it's actually probably possible to make it not required).

In testing though, the property setters don't appear to be the main bottleneck to Canvas text performance. It's more in the layout and texture generation area. This code is largely pulled verbatim from L2 but it's not getting the same JIT optimizations for some reason.

@elsassph
Copy link
Contributor

elsassph commented Jul 5, 2024

Ok it may not be the worst problem but the prop setters are a massive over-engineering and memory inefficient:

  • you have 2 renderers, unlikely to be more,
  • you can call invalidate('contain') so the renderer can apply special invalidation for a few props.

For deoptimisations, I think it's possible to run Chrome with a certain flag to log those (it might be super verbose to inspect though).

@wouterlucas wouterlucas changed the base branch from fix/canvas2d-fonts-performance to main July 22, 2024 10:06
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.

3 participants