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

Eliminate heap allocations for gradient color conversions #154650

Closed
gaaclarke opened this issue Sep 4, 2024 · 8 comments · Fixed by flutter/engine#57143
Closed

Eliminate heap allocations for gradient color conversions #154650

gaaclarke opened this issue Sep 4, 2024 · 8 comments · Fixed by flutter/engine#57143
Labels
P1 High-priority issues at the top of the work list team-engine Owned by Engine team triaged-engine Triaged by Engine team

Comments

@gaaclarke
Copy link
Member

gaaclarke commented Sep 4, 2024

Spawned from flutter/engine#54748

When colors of a gradient are communicated from Dart to C++ they are coming in as arrays of floats which represent the color components of the colors in extended sRGB. When they get stored in the display list they are stored as an array of DlColor. As a temporary measure the floats are converted to DlColor on the heap, then sent into the display list for storage. We can't do it on the stack because the number of colors is unlimited.

I looked into trying to switch the DlColorSource factory methods to take in DlScalar*'s, that way it could be converted to DlColors while we were iterating over them, avoiding the heap allocation. That doesn't work however because there is a second client of those functions, the DlColorSource::shared() methods which does not have DlScalar*, they already have their colors stored as DlColor*. The only way to make that work would have been to refactor all the API's to take in an iterator. That's kind of messy in C++ since it would mean surfacing more logic into templates and headers, or incurring dynamic dispatch cost.

@jonahwilliams proposes we should do a larger refactor where we change the storage of the DlColorSource to instead store its colors as a DlScalar*. That way both clients of the DlColorColorSource::Make* functions can pass in DlScalar*. He noted that storing the colorspace per color may be redundant anyways.

cc @flar

@gaaclarke
Copy link
Member Author

gaaclarke commented Sep 4, 2024

I think potentially the work in flutter/engine#54963 can be continued, we just have to rewrite the storage of the classes to also be DlScalars instead of DlColors.

@gaaclarke
Copy link
Member Author

@flar I'm just reorganizing all the work items I have left for wide gamut framework. If you are interested I can hand this over to you. I have a few other things I could be working on. Let me know if you are interested.

@flar
Copy link
Contributor

flar commented Sep 5, 2024

I'll take this on, I'm going to have to modify these classes to deal with DlPoint/DlMatrix, etc. anyway...

@jonahwilliams jonahwilliams added P1 High-priority issues at the top of the work list triaged-engine Triaged by Engine team labels Sep 9, 2024
@gaaclarke
Copy link
Member Author

I looked into this a bit. The flow is like so:

CanvasGradient::initLinear - converts DlScalar[] to DlColor[] (with heap allocation)
  DlColorSource::MakeLinear - calculates size of DlLinearGradientColorSource based on size of DlColor[]
    DlLinearGradientColorSource::DlLinearGradientColorSource - memcpy DlColor[] into this

The options are:

  1. Use template iterators - this avoids the extra heap allocation but interrupts a faster memcpy operation, it also complicates the code by moving more code into headers
  2. Use virtual iterators - this introduces dynamic dispatch for each color, but removes the heap allocation
  3. std::move DlColor[] - This makes the heap allocation not wasted since it gets passed onto the color source, thus removing a memcpy. This changes the logic of DlLinearGradientColorSource a bit, probably for the better since it's weird not having a fixed size instance. The locality of the instance will be broken up though.
  4. Switch the storage of DlLinearGradientColorSource to DlScalar - this would be unfortunate to degrade the class to store components instead of instances.
  5. Cap the size of supported colors - it's kind of silly to bend over backwards for a feature we don't even want anyways
  6. Use a fixed array on the stack unless the number of colors is over a threshold - this would capture most use cases but was already vetoed as an option.
  7. Reuse the vector across invocations of CanvasGradient::initLinear - caching values can always lead to weird bugs and I don't know if there is a good owner for this vector.

Looking at all the options I'm still partial to # 6.

@flar
Copy link
Contributor

flar commented Dec 10, 2024

Another option is to have DlGradient variants that take the DlScalar representation as input and initialize and convert directly into the allocated storage?

@jonahwilliams
Copy link
Member

Yeah I would be in favor of that over the other approaches

@gaaclarke
Copy link
Member Author

Okay, did that but used iterators in the .cc file to remove duplication, a blend of 2 things proposed.

gaaclarke added a commit to flutter/engine that referenced this issue Dec 11, 2024
… dart and display list (#57108)

issue: flutter/flutter#154650

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide] and the [C++,
Objective-C, Java style guides].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I added new tests to check the change I am making or feature I am
adding, or the PR is [test-exempt]. See [testing the engine] for
instructions on writing and running engine tests.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I signed the [CLA].
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md
[C++, Objective-C, Java style guides]:
https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
[testing the engine]:
https://github.com/flutter/engine/blob/main/docs/testing/Testing-the-engine.md
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md
gaaclarke added a commit to flutter/engine that referenced this issue Dec 11, 2024
)

fixes flutter/flutter#154650

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide] and the [C++,
Objective-C, Java style guides].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I added new tests to check the change I am making or feature I am
adding, or the PR is [test-exempt]. See [testing the engine] for
instructions on writing and running engine tests.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I signed the [CLA].
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md
[C++, Objective-C, Java style guides]:
https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
[testing the engine]:
https://github.com/flutter/engine/blob/main/docs/testing/Testing-the-engine.md
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md
nick9822 pushed a commit to nick9822/flutter that referenced this issue Dec 18, 2024
… dart and display list (flutter/engine#57108)

issue: flutter#154650

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide] and the [C++,
Objective-C, Java style guides].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I added new tests to check the change I am making or feature I am
adding, or the PR is [test-exempt]. See [testing the engine] for
instructions on writing and running engine tests.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I signed the [CLA].
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md
[C++, Objective-C, Java style guides]:
https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
[testing the engine]:
https://github.com/flutter/engine/blob/main/docs/testing/Testing-the-engine.md
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md
nick9822 pushed a commit to nick9822/flutter that referenced this issue Dec 18, 2024
…tter/engine#57143)

fixes flutter#154650

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide] and the [C++,
Objective-C, Java style guides].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I added new tests to check the change I am making or feature I am
adding, or the PR is [test-exempt]. See [testing the engine] for
instructions on writing and running engine tests.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I signed the [CLA].
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md
[C++, Objective-C, Java style guides]:
https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
[testing the engine]:
https://github.com/flutter/engine/blob/main/docs/testing/Testing-the-engine.md
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md
Copy link

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
P1 High-priority issues at the top of the work list team-engine Owned by Engine team triaged-engine Triaged by Engine team
Projects
None yet
3 participants