Skip to content

Commit

Permalink
[Impeller] Add support for specialization constants (III). (#47765)
Browse files Browse the repository at this point in the history
Reland of #47432

Also includes:

#47617
#47637

Fixes the performance on iOS by removing blocking on compilation of shaders. From local testing this has identical before/after numbers. Additional, ensures that we don't unecessarily specialize vertex shaders and notes this restriction in the documentation.

Adds support for Specialization constants to Impeller for our usage in the engine. A motivating example has been added in the impeller markdown docs.

Fixes flutter/flutter#136210
Fixes flutter/flutter#119357

Investigating: flutter/flutter#138028
  • Loading branch information
jonahwilliams committed Nov 8, 2023
1 parent b67259c commit 9f32e03
Show file tree
Hide file tree
Showing 64 changed files with 1,247 additions and 3,983 deletions.
74 changes: 8 additions & 66 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -3136,42 +3136,13 @@ ORIGIN: ../../../flutter/impeller/entity/inline_pass_context.cc + ../../../flutt
ORIGIN: ../../../flutter/impeller/entity/inline_pass_context.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/render_target_cache.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/render_target_cache.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.glsl + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_color.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_colorburn.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_colordodge.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_darken.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_difference.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_exclusion.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_hardlight.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_hue.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_lighten.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_luminosity.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_multiply.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_overlay.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_saturation.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_screen.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_softlight.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/blend.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/blend.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend.glsl + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_color.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_colorburn.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_colordodge.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_darken.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_difference.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_exclusion.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_hardlight.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_hue.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_lighten.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_luminosity.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_multiply.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_overlay.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_saturation.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_screen.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_softlight.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/blend_select.glsl + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/framebuffer_blend.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/framebuffer_blend.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/porter_duff_blend.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/porter_duff_blend.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/border_mask_blur.frag + ../../../flutter/LICENSE
Expand Down Expand Up @@ -5921,42 +5892,13 @@ FILE: ../../../flutter/impeller/entity/inline_pass_context.cc
FILE: ../../../flutter/impeller/entity/inline_pass_context.h
FILE: ../../../flutter/impeller/entity/render_target_cache.cc
FILE: ../../../flutter/impeller/entity/render_target_cache.h
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.glsl
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend.vert
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_color.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_colorburn.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_colordodge.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_darken.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_difference.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_exclusion.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_hardlight.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_hue.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_lighten.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_luminosity.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_multiply.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_overlay.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_saturation.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_screen.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/advanced_blend_softlight.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/blend.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/blend.vert
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend.glsl
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend.vert
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_color.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_colorburn.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_colordodge.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_darken.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_difference.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_exclusion.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_hardlight.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_hue.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_lighten.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_luminosity.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_multiply.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_overlay.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_saturation.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_screen.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_softlight.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/blend_select.glsl
FILE: ../../../flutter/impeller/entity/shaders/blending/framebuffer_blend.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/framebuffer_blend.vert
FILE: ../../../flutter/impeller/entity/shaders/blending/porter_duff_blend.frag
FILE: ../../../flutter/impeller/entity/shaders/blending/porter_duff_blend.vert
FILE: ../../../flutter/impeller/entity/shaders/border_mask_blur.frag
Expand Down
4 changes: 0 additions & 4 deletions impeller/base/comparable.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,8 @@
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <type_traits>

#include "flutter/fml/hash_combine.h"
#include "flutter/fml/macros.h"

namespace impeller {

struct UniqueID {
Expand Down
94 changes: 94 additions & 0 deletions impeller/docs/specialization_constants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Specialization Constants

A specialization constant is a named variable that is known to be constant at runtime but not when the shader is authored. These variables are bound to specific values when the shader is compiled on application start up and allow the backend to perform optimizations such as branch elimination and constant folding.

Specialization constants have two possible benefits when used in a shader:

* Improving performance, by removing branching and conditional code.
* Code organization/size, by removing the number of shader source files required.

These goals are related: The number of shaders can be reduce by adding runtime branching to create more generic shaders. Alternatively, branching can be reduced by adding more specialized shader variants. Specialization constants provide a happy medium where the source files can be combined with branching but done so in a way that has no runtime cost.

## Example Usage

Consider the case of the "decal" texture sampling mode. This is implement via clamp-to-border with
a border color set to transparent black. While this functionality is well supported on the Metal and
Vulkan backends, the GLES backend needs to support devices that do not have this extension. As a
result, the following code was used to conditionally decal:

```glsl
// Decal sample if necessary.
vec4 Sample(sampler2D sampler, vec2 coord) {
#ifdef GLES
return IPSampleDecal(sampler, coord)
#else
return texture(sampler, coord);
#endif
}
```

This works great as long as we know that the GLES backend can never do the decal sample mode. This is also "free" as the ifdef branch is evaluated in the compiler. But eventually, we added a runtime check for decal mode as we need to support this on GLES. So the code turned into (approximately) the following:

```glsl
#ifdef GLES
uniform float supports_decal;
#endif
// Decal sample if necessary.
vec4 Sample(sampler2D sampler, vec2 coord) {
#ifdef GLES
if (supports_decal) {
return texture(sampler, coord);
}
return IPSampleDecal(sampler, coord)
#else
return texture(sampler, coord);
#endif
}
```

Now we've got decal support, but we've also got new problems:

* The code is actually quite messy. We have to track different uniform values depending on the backend.
* The GLES backend is still paying some cost for branching, even though we "know" that decal is or isn't supported when the shader is compiled.

### Specialization constants to the rescue

Instead of using a runtime check, we can create a specialization constant that is set when compiling the
shader. This constant will be `1` if decal is supported and `0` otherwise.

```glsl
layout(constant_id = 0) const int supports_decal = 1;
vec4 Sample(sampler2D sampler, vec2 coord) {
if (supports_decal) {
return texture(sampler, coord);
}
return IPSampleDecal(sampler, coord)
}
```

Immediately we realize a number of benefits:

* Code is the same across all backends
* Runtime branching cost is removed as the branch is compiled out.


## Implementation

Only 32bit ints are supported as const values and can be used to represent:

* true/false via 0/1.
* function selection, such as advanced blends. The specialization value maps to a specific blend function. For example, 0 maps to screen and 1 to overlay via a giant if/else macro.
* Only fragment shaders can be specialized. This limitation could be removed with more investment.

*AVOID* adding specialization constants for color values or anything more complex.

Specialization constants are provided to the CreateDefault argument in content_context.cc and aren't a
part of variants. This is intentional: specialization constants shouldn't be used to create (potentially unlimited) runtime variants of a shader.

Backend specific information:
* In the Metal backend, the specialization constants are mapped to a MTLFunctionConstantValues. See also: https://developer.apple.com/documentation/metal/using_function_specialization_to_build_pipeline_variants?language=objc
* In the Vulkan backend, the specialization constants are mapped to VkSpecializationINfo. See also: https://blogs.igalia.com/itoral/2018/03/20/improving-shader-performance-with-vulkans-specialization-constants/
* In the GLES backend, the SPIRV Cross compiler will generate defines named `#ifdef SPIRV_CROSS_CONSTANT_i`, where i is the index of constant. The Impeller runtime will insert `#define SPIRV_CROSS_CONSTANT_i` in the header of the shader.
34 changes: 3 additions & 31 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,7 @@ impeller_shaders("entity_shaders") {

shaders = [
"shaders/blending/advanced_blend.vert",
"shaders/blending/advanced_blend_color.frag",
"shaders/blending/advanced_blend_colorburn.frag",
"shaders/blending/advanced_blend_colordodge.frag",
"shaders/blending/advanced_blend_darken.frag",
"shaders/blending/advanced_blend_difference.frag",
"shaders/blending/advanced_blend_exclusion.frag",
"shaders/blending/advanced_blend_hardlight.frag",
"shaders/blending/advanced_blend_hue.frag",
"shaders/blending/advanced_blend_lighten.frag",
"shaders/blending/advanced_blend_luminosity.frag",
"shaders/blending/advanced_blend_multiply.frag",
"shaders/blending/advanced_blend_overlay.frag",
"shaders/blending/advanced_blend_saturation.frag",
"shaders/blending/advanced_blend_screen.frag",
"shaders/blending/advanced_blend_softlight.frag",
"shaders/blending/advanced_blend.frag",
"shaders/blending/blend.frag",
"shaders/blending/blend.vert",
"shaders/border_mask_blur.frag",
Expand Down Expand Up @@ -115,22 +101,8 @@ impeller_shaders("framebuffer_blend_entity_shaders") {
}

shaders = [
"shaders/blending/ios/framebuffer_blend.vert",
"shaders/blending/ios/framebuffer_blend_color.frag",
"shaders/blending/ios/framebuffer_blend_colorburn.frag",
"shaders/blending/ios/framebuffer_blend_colordodge.frag",
"shaders/blending/ios/framebuffer_blend_darken.frag",
"shaders/blending/ios/framebuffer_blend_difference.frag",
"shaders/blending/ios/framebuffer_blend_exclusion.frag",
"shaders/blending/ios/framebuffer_blend_hardlight.frag",
"shaders/blending/ios/framebuffer_blend_hue.frag",
"shaders/blending/ios/framebuffer_blend_lighten.frag",
"shaders/blending/ios/framebuffer_blend_luminosity.frag",
"shaders/blending/ios/framebuffer_blend_multiply.frag",
"shaders/blending/ios/framebuffer_blend_overlay.frag",
"shaders/blending/ios/framebuffer_blend_saturation.frag",
"shaders/blending/ios/framebuffer_blend_screen.frag",
"shaders/blending/ios/framebuffer_blend_softlight.frag",
"shaders/blending/framebuffer_blend.vert",
"shaders/blending/framebuffer_blend.frag",
]
}

Expand Down
2 changes: 0 additions & 2 deletions impeller/entity/contents/atlas_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,6 @@ bool AtlasContents::Render(const ContentContext& renderer,
dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
}
frag_info.supports_decal_sampler_address_mode =
renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode();
auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
dst_sampler_descriptor);
FS::BindTextureSamplerDst(cmd, texture_, dst_sampler);
Expand Down

0 comments on commit 9f32e03

Please sign in to comment.