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

[Impeller] Support managed static branches for ImpellerC shaders. #116103

Closed
bdero opened this issue Nov 28, 2022 · 4 comments
Closed

[Impeller] Support managed static branches for ImpellerC shaders. #116103

bdero opened this issue Nov 28, 2022 · 4 comments
Labels
c: proposal A detailed proposal for a change to Flutter e: impeller Impeller rendering backend issues and features requests engine flutter/engine repository. See also e: labels. P2 Important issues not at the top of the work list team-engine Owned by Engine team triaged-engine Triaged by Engine team

Comments

@bdero
Copy link
Member

bdero commented Nov 28, 2022

When authoring shaders which perform a given rendering task, it's often convenient to customize the task at runtime by dynamically branching on a discrete uniform input (constant with respect to the pipeline invocation). Shaders which are extended this way are both easy to author and maintain.
For example, a uniform could be used to turn on/off an optional rendering feature, or to enumerate different UV mapping behaviors when sampling from bound textures.

This technique can work great for simple branches that contribute minimal constant time overhead to the computation, but can quickly pose a performance problem if the branch is nontrivial and/or needs to be used in a superconstant code path.

  • Non-trivial branch: By non-trivial branch, I really mean many simple branches clumped together. It's not uncommon to run into cases where a large enum is used to customize a rendering subtask in Flutter. For example: When Impeller receives a DrawVertices command, it needs to blend the input colors with the paint color; all of Flutter's 27 blend functions are supported (at the time of writing, we perform this particular blend on the CPU, but performing this per-vertex operation on the GPU would scale better given parallelism in the vertex pipeline stage). GLSL does not support function pointers, rendering dynamic dispatch impossible for shaders targeting the GLES backend (as well as any other backend when GLSL is used as the source language). This means dispatching with a blend enum in a shader would require something like a 27 item switch (although this could be reduced to at most 5 branches in a binary decision tree).
  • Superconstant context: Convolution shaders such as Impeller's two pass Gaussian blur contain an expensive code path whose execution count scales linearly with respect to a uniform kernel radius. Even seemingly small overhead savings in such code paths can make a meaningful performance impact, as the computation may be executed hundreds or thousands of times per fragment per render pass.

The fast alternative to uniform dynamic branching is to produce individual specialized shaders for each branch case. Then, the correct shader can be chosen at render time on the CPU.

However, converting uniform branches to static branches is currently an incredibly cumbersome process that outputs maintainability nightmares.

  • A cumbersome time sink: Static shader variants need to be authored as individual shader files with individual names. The resulting shaders end up looking very different from the original with parts spread across a number of files connected through imports. Each of these shaders need to be hooked up as separate pipeline variants in the Impeller Entities renderer, which is quite a lot of boilerplate for each discrete enum option. This problem is exacerbated in cases where static shader variants are made for multiple distinct enums resulting in a combinatorial explosion.
  • Unmaintainable results: Code between the variant shaders can be shared through relative path importing, but the resulting code is fragmented across at least as many files as there are shader variants. Per-pipeline boilerplate in the Entities renderer means updating discrete variants is an error prone slog. Need to add a binary static branch to your 4 existing variants? Well, you need to make 4 more shaders and probably rename all of the existing boilerplate so that everything continues making sense, see you in a couple of hours.

These are the problems that managed static branches can solve. Static branching should be no more difficult (and perhaps even easier) than dynamic branching on discrete uniforms. For shader authors, defining and using static branches can be made trivial.

@bdero bdero added engine flutter/engine repository. See also e: labels. e: impeller Impeller rendering backend issues and features requests labels Nov 28, 2022
@bdero bdero self-assigned this Nov 28, 2022
@bdero
Copy link
Member Author

bdero commented Nov 28, 2022

I'm currently working on a detailed proposal around this, but the basic idea is:

  • Shaders get a new special #pragma variant NAME [ENTRIES] syntax for defining variant enums.
  • The enums entries are used in the shader like normal preprocessor defines with e.g. #if.
  • ImpellerC outputs a shader for each variant, but one set of shared headers/metadata.
  • Shaders with variant enums have a special naming convention for function registration.
  • Metadata/headers have info about the static variant enums, including a struct with the variant enums as members which can be used to build variant names for function lookup.
  • The ContentContext Pipeline resolution utilities accept the variant enum struct as part of the descriptor options and is used to lookup the shader.

@jonahwilliams
Copy link
Member

In some cases, these variants will end up with different uniforms too. See the variants in https://github.com/flutter/engine/pull/37925/files

@chinmaygarde chinmaygarde added the P2 Important issues not at the top of the work list label Nov 28, 2022
@bdero bdero added the c: proposal A detailed proposal for a change to Flutter label Nov 29, 2022
@flutter-triage-bot flutter-triage-bot bot added team-engine Owned by Engine team triaged-engine Triaged by Engine team labels Jul 8, 2023
@jonahwilliams
Copy link
Member

I think with specialization constants, we've effectively covered all of the use cases we'd need this for: #119357

If there are other usecases not covered there, please reopen

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 Mar 20, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
c: proposal A detailed proposal for a change to Flutter e: impeller Impeller rendering backend issues and features requests engine flutter/engine repository. See also e: labels. P2 Important issues not at the top of the work list team-engine Owned by Engine team triaged-engine Triaged by Engine team
Projects
No open projects
Status: ⚡ Performance
Development

No branches or pull requests

3 participants