Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
self code review.
  • Loading branch information
jonahwilliams committed Oct 30, 2023
commit 432700d614d4fd91951865de176fd08d36adbc99
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
90 changes: 90 additions & 0 deletions impeller/docs/specialization_constants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Specialization Constants

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 interrrelated, we can always reduce the number of shaders by creating
ubershaders. Or we can reduce branching by adding more shader variants. Specialization
constants provide a happy medium where the source files would be highly reptitive.

## 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

Currently the only constant values that are support are 32bit ints. This should be sufficient for now as we
should generally only use these values for true/false or select-style constants. Please don't try to add
constant color values or anything like that.

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.
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
70 changes: 36 additions & 34 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,9 @@ void ContentContextOptions::ApplyToPipelineDescriptor(

template <typename PipelineT>
static std::unique_ptr<PipelineT> CreateDefaultPipeline(
const Context& context,
const std::map<SpecializationConstant, uint64_t>& constants = {}) {
const Context& context) {
auto desc =
PipelineT::Builder::MakeDefaultPipelineDescriptor(context, constants);
PipelineT::Builder::MakeDefaultPipelineDescriptor(context);
if (!desc.has_value()) {
return nullptr;
}
Expand Down Expand Up @@ -199,6 +198,8 @@ ContentContext::ContentContext(
.primitive_type = PrimitiveType::kTriangleStrip,
.color_attachment_pixel_format =
context_->GetCapabilities()->GetDefaultColorFormat()};
const auto supports_decal =
context_->GetCapabilities()->SupportsDecalSamplerAddressMode();

#ifdef IMPELLER_DEBUG
checkerboard_pipelines_.CreateDefault(*context_, options);
Expand All @@ -221,96 +222,96 @@ ContentContext::ContentContext(
if (context_->GetCapabilities()->SupportsFramebufferFetch()) {
framebuffer_blend_color_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kColor)});
{static_cast<int32_t>(BlendSelectValues::kColor), supports_decal});
framebuffer_blend_colorburn_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kColorBurn)});
{static_cast<int32_t>(BlendSelectValues::kColorBurn), supports_decal});
framebuffer_blend_colordodge_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kColorDodge)});
{static_cast<int32_t>(BlendSelectValues::kColorDodge), supports_decal});
framebuffer_blend_darken_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kDarken)});
{static_cast<int32_t>(BlendSelectValues::kDarken), supports_decal});
framebuffer_blend_difference_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kDifference)});
{static_cast<int32_t>(BlendSelectValues::kDifference), supports_decal});
framebuffer_blend_exclusion_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kExclusion)});
{static_cast<int32_t>(BlendSelectValues::kExclusion), supports_decal});
framebuffer_blend_hardlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kHardLight)});
{static_cast<int32_t>(BlendSelectValues::kHardLight), supports_decal});
framebuffer_blend_hue_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kHue)});
{static_cast<int32_t>(BlendSelectValues::kHue), supports_decal});
framebuffer_blend_lighten_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kLighten)});
{static_cast<int32_t>(BlendSelectValues::kLighten), supports_decal});
framebuffer_blend_luminosity_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kLuminosity)});
{static_cast<int32_t>(BlendSelectValues::kLuminosity), supports_decal});
framebuffer_blend_multiply_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kMultiply)});
{static_cast<int32_t>(BlendSelectValues::kMultiply), supports_decal});
framebuffer_blend_overlay_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kOverlay)});
{static_cast<int32_t>(BlendSelectValues::kOverlay), supports_decal});
framebuffer_blend_saturation_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kSaturation)});
{static_cast<int32_t>(BlendSelectValues::kSaturation), supports_decal});
framebuffer_blend_screen_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kScreen)});
{static_cast<int32_t>(BlendSelectValues::kScreen), supports_decal});
framebuffer_blend_softlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kSoftLight)});
{static_cast<int32_t>(BlendSelectValues::kSoftLight), supports_decal});
}

blend_color_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kColor)});
{static_cast<int32_t>(BlendSelectValues::kColor), supports_decal});
blend_colorburn_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kColorBurn)});
{static_cast<int32_t>(BlendSelectValues::kColorBurn), supports_decal});
blend_colordodge_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kColorDodge)});
{static_cast<int32_t>(BlendSelectValues::kColorDodge), supports_decal});
blend_darken_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kDarken)});
{static_cast<int32_t>(BlendSelectValues::kDarken), supports_decal});
blend_difference_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kDifference)});
{static_cast<int32_t>(BlendSelectValues::kDifference), supports_decal});
blend_exclusion_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kExclusion)});
{static_cast<int32_t>(BlendSelectValues::kExclusion), supports_decal});
blend_hardlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kHardLight)});
{static_cast<int32_t>(BlendSelectValues::kHardLight), supports_decal});
blend_hue_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kHue)});
{static_cast<int32_t>(BlendSelectValues::kHue), supports_decal});
blend_lighten_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kLighten)});
{static_cast<int32_t>(BlendSelectValues::kLighten), supports_decal});
blend_luminosity_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kLuminosity)});
{static_cast<int32_t>(BlendSelectValues::kLuminosity), supports_decal});
blend_multiply_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kMultiply)});
{static_cast<int32_t>(BlendSelectValues::kMultiply), supports_decal});
blend_overlay_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kOverlay)});
{static_cast<int32_t>(BlendSelectValues::kOverlay), supports_decal});
blend_saturation_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kSaturation)});
{static_cast<int32_t>(BlendSelectValues::kSaturation), supports_decal});
blend_screen_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kScreen)});
{static_cast<int32_t>(BlendSelectValues::kScreen), supports_decal});
blend_softlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int>(BlendSelectValues::kSoftLight)});
{static_cast<int32_t>(BlendSelectValues::kSoftLight), supports_decal});

rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip);
texture_blend_pipelines_.CreateDefault(*context_, options);
Expand All @@ -333,7 +334,8 @@ ContentContext::ContentContext(
glyph_atlas_color_pipelines_.CreateDefault(*context_, options);
geometry_color_pipelines_.CreateDefault(*context_, options);
yuv_to_rgb_filter_pipelines_.CreateDefault(*context_, options_trianglestrip);
porter_duff_blend_pipelines_.CreateDefault(*context_, options_trianglestrip);
porter_duff_blend_pipelines_.CreateDefault(*context_, options_trianglestrip,
{supports_decal});
// GLES only shader.
#ifdef IMPELLER_ENABLE_OPENGLES
if (GetContext()->GetBackendType() == Context::BackendType::kOpenGLES) {
Expand Down
2 changes: 1 addition & 1 deletion impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ class ContentContext {

void CreateDefault(const Context& context,
const ContentContextOptions& options,
const std::initializer_list<int>& constants = {}) {
const std::initializer_list<int32_t>& constants = {}) {
auto desc =
PipelineT::Builder::MakeDefaultPipelineDescriptor(context, constants);
if (!desc.has_value()) {
Expand Down
6 changes: 0 additions & 6 deletions impeller/entity/contents/filters/blend_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,6 @@ static std::optional<Entity> AdvancedBlend(
dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
}
blend_info.supports_decal_sampler_address_mode =
renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode();
auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
dst_sampler_descriptor);
FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
Expand Down Expand Up @@ -354,8 +352,6 @@ std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
}
blend_info.supports_decal_sampler_address_mode =
renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode();
auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
dst_sampler_descriptor);
FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
Expand Down Expand Up @@ -478,8 +474,6 @@ std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
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, dst_snapshot->texture, dst_sampler);
Expand Down
19 changes: 10 additions & 9 deletions impeller/entity/shaders/blending/advanced_blend.frag
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
#include <impeller/color.glsl>
#include <impeller/texture.glsl>
#include <impeller/types.glsl>
#include "blend_dispatch.glsl"
#include "blend_select.glsl"

layout(constant_id = 0) const int blend_type = 0;
layout(constant_id = 1) const int supports_decal = 1;

uniform BlendInfo {
float16_t dst_input_alpha;
Expand All @@ -26,17 +29,14 @@ in vec2 v_src_texture_coords;
out f16vec4 frag_color;

f16vec4 Sample(f16sampler2D texture_sampler, vec2 texture_coords) {
#ifdef IMPELLER_TARGET_OPENGLES
if (blend_info.supports_decal_sampler_address_mode > 0.0) {
if (supports_decal > 0.0) {
return texture(texture_sampler, texture_coords);
} else {
return IPHalfSampleDecal(texture_sampler, texture_coords);
}
#else
return texture(texture_sampler, texture_coords);
#endif
return IPHalfSampleDecal(texture_sampler, texture_coords);
}

AdvancedBlend(blend_type);

void main() {
f16vec4 dst_sample = Sample(texture_sampler_dst, // sampler
v_dst_texture_coords // texture coordinates
Expand All @@ -51,6 +51,7 @@ void main() {
) *
blend_info.src_input_alpha;

f16vec4 blended = mix(src, f16vec4(Blend(dst.rgb, src.rgb), dst.a), dst.a);
f16vec3 blend_result = Blend(dst.rgb, src.rgb);
f16vec4 blended = mix(src, f16vec4(blend_result, dst.a), dst.a);
frag_color = mix(dst_sample, blended, src.a);
}
Loading