diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 61cb7b1a2d696..df13666e28c0b 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1278,6 +1278,7 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_ 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/porter_duff_blend.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/border_mask_blur.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/border_mask_blur.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/color_matrix_color_filter.frag + ../../../flutter/LICENSE @@ -3867,6 +3868,7 @@ FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_ov 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/porter_duff_blend.frag FILE: ../../../flutter/impeller/entity/shaders/border_mask_blur.frag FILE: ../../../flutter/impeller/entity/shaders/border_mask_blur.vert FILE: ../../../flutter/impeller/entity/shaders/color_matrix_color_filter.frag diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 215e276fb7243..765260d28cd37 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -2035,6 +2035,26 @@ TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterAndDrawsCorrectly) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, TranslucentSaveLayerWithColorImageFilterAndDrawsCorrectly) { + Canvas canvas; + + canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()}); + + canvas.SaveLayer({ + .color = Color::Black().WithAlpha(0.5), + .color_filter = + [](FilterInput::Ref input) { + return ColorFilterContents::MakeBlend( + BlendMode::kDestinationOver, {std::move(input)}, Color::Red()); + }, + }); + + canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()}); + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_P(AiksTest, TranslucentSaveLayerImageDrawsCorrectly) { Canvas canvas; diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index 21db229817f51..330b7b13daaeb 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -59,7 +59,7 @@ std::shared_ptr Paint::WithFilters( std::shared_ptr input, std::optional is_solid_color) const { bool is_solid_color_val = is_solid_color.value_or(!color_source); - input = WithColorFilter(input); + input = WithColorFilter(input, /*absorb_opacity=*/true); input = WithInvertFilter(input); input = WithMaskBlur(input, is_solid_color_val, Matrix()); input = WithImageFilter(input, Matrix(), /*is_subpass=*/false); diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 54af14f3796a6..8e5459bc8f3dd 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -70,6 +70,7 @@ impeller_shaders("entity_shaders") { "shaders/vertices.frag", "shaders/yuv_to_rgb_filter.frag", "shaders/yuv_to_rgb_filter.vert", + "shaders/blending/porter_duff_blend.frag", ] } diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index f88c674fbc408..1afde11e40273 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -287,6 +287,8 @@ ContentContext::ContentContext(std::shared_ptr context) CreateDefaultPipeline(*context_); yuv_to_rgb_filter_pipelines_[{}] = CreateDefaultPipeline(*context_); + porter_duff_blend_pipelines_[{}] = + CreateDefaultPipeline(*context_); if (solid_fill_pipelines_[{}]->GetDescriptor().has_value()) { auto clip_pipeline_descriptor = diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index e5a8189f85518..73323c92084a0 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -35,6 +35,7 @@ #include "impeller/entity/linear_to_srgb_filter.vert.h" #include "impeller/entity/morphology_filter.frag.h" #include "impeller/entity/morphology_filter.vert.h" +#include "impeller/entity/porter_duff_blend.frag.h" #include "impeller/entity/radial_gradient_fill.frag.h" #include "impeller/entity/rrect_blur.frag.h" #include "impeller/entity/rrect_blur.vert.h" @@ -125,7 +126,6 @@ using RadialGradientSSBOFillPipeline = using SweepGradientSSBOFillPipeline = RenderPipelineT; -using BlendPipeline = RenderPipelineT; using RRectBlurPipeline = RenderPipelineT; using BlendPipeline = RenderPipelineT; @@ -165,6 +165,8 @@ using GlyphAtlasPipeline = RenderPipelineT; using GlyphAtlasSdfPipeline = RenderPipelineT; +using PorterDuffBlendPipeline = + RenderPipelineT; // Instead of requiring new shaders for clips, the solid fill stages are used // to redirect writing to the stencil instead of color attachments. using ClipPipeline = @@ -469,6 +471,11 @@ class ContentContext { return GetPipeline(yuv_to_rgb_filter_pipelines_, opts); } + std::shared_ptr> GetPorterDuffBlendPipeline( + ContentContextOptions opts) const { + return GetPipeline(porter_duff_blend_pipelines_, opts); + } + // Advanced blends. std::shared_ptr> GetBlendColorPipeline( @@ -705,6 +712,7 @@ class ContentContext { mutable Variants glyph_atlas_sdf_pipelines_; mutable Variants geometry_color_pipelines_; mutable Variants yuv_to_rgb_filter_pipelines_; + mutable Variants porter_duff_blend_pipelines_; // Advanced blends. mutable Variants blend_color_pipelines_; mutable Variants blend_colorburn_pipelines_; diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index c5e7c8c63cf8d..10477d2e6272e 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -179,7 +179,7 @@ static std::optional AdvancedBlend( entity.GetBlendMode(), entity.GetStencilDepth()); } -std::optional BlendFilterContents::CreateForegroundBlend( +std::optional BlendFilterContents::CreateForegroundAdvancedBlend( const std::shared_ptr& input, const ContentContext& renderer, const Entity& entity, @@ -318,21 +318,145 @@ std::optional BlendFilterContents::CreateForegroundBlend( auto contents = AnonymousContents::Make(render_proc, coverage_proc); - // If there is pending opacity but it was not absorbed by this entity, we have - // to convert this back to a snapshot so it can be passed on. This generally - // implies that there is another filter about to run, so we'd perform this - // operation anyway. - auto potential_opacity = alpha.value_or(1.0) * dst_snapshot->opacity; - if (!absorb_opacity && potential_opacity < 1.0) { - auto result_snapshot = contents->RenderToSnapshot(renderer, entity); - if (!result_snapshot.has_value()) { - return std::nullopt; - } - result_snapshot->opacity = potential_opacity; - return Entity::FromSnapshot(result_snapshot.value(), entity.GetBlendMode(), + Entity sub_entity; + sub_entity.SetContents(std::move(contents)); + sub_entity.SetStencilDepth(entity.GetStencilDepth()); + sub_entity.SetTransformation(entity.GetTransformation()); + + return sub_entity; +} + +constexpr std::array, 15> kPorterDuffCoefficients = {{ + {0, 0, 0, 0, 0}, // Clear + {1, 0, 0, 0, 0}, // Source + {0, 0, 1, 0, 0}, // Destination + {1, 0, 1, -1, 0}, // SourceOver + {1, -1, 1, 0, 0}, // DestinationOver + {0, 1, 0, 0, 0}, // SourceIn + {0, 0, 0, 1, 0}, // DestinationIn + {1, -1, 0, 0, 0}, // SourceOut + {0, 0, 1, -1, 0}, // DestinationOut + {0, 1, 1, -1, 0}, // SourceATop + {1, -1, 0, 1, 0}, // DestinationATop + {1, -1, 1, -1, 0}, // Xor + {1, 0, 1, 0, 0}, // Plus + {0, 0, 0, 0, 1}, // Modulate + {0, 0, 1, 0, -1}, // Screen +}}; + +std::optional BlendFilterContents::CreateForegroundPorterDuffBlend( + const std::shared_ptr& input, + const ContentContext& renderer, + const Entity& entity, + const Rect& coverage, + Color foreground_color, + BlendMode blend_mode, + std::optional alpha, + bool absorb_opacity) const { + auto dst_snapshot = input->GetSnapshot(renderer, entity); + if (!dst_snapshot.has_value()) { + return std::nullopt; + } + + if (blend_mode == BlendMode::kClear) { + return std::nullopt; + } + + if (blend_mode == BlendMode::kDestination) { + return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(), entity.GetStencilDepth()); } + if (blend_mode == BlendMode::kSource) { + auto contents = std::make_shared(); + contents->SetGeometry(Geometry::MakeRect(coverage)); + contents->SetColor(foreground_color); + + Entity foreground_entity; + foreground_entity.SetBlendMode(entity.GetBlendMode()); + foreground_entity.SetStencilDepth(entity.GetStencilDepth()); + foreground_entity.SetContents(std::move(contents)); + return foreground_entity; + } + + RenderProc render_proc = [foreground_color, coverage, dst_snapshot, + blend_mode, absorb_opacity, alpha]( + const ContentContext& renderer, + const Entity& entity, RenderPass& pass) -> bool { + using VS = PorterDuffBlendPipeline::VertexShader; + using FS = PorterDuffBlendPipeline::FragmentShader; + + auto& host_buffer = pass.GetTransientsBuffer(); + + auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage); + if (!maybe_dst_uvs.has_value()) { + return false; + } + auto dst_uvs = maybe_dst_uvs.value(); + + auto size = coverage.size; + auto origin = coverage.origin; + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {origin, dst_uvs[0]}, + {Point(origin.x + size.width, origin.y), dst_uvs[1]}, + {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3]}, + {origin, dst_uvs[0]}, + {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3]}, + {Point(origin.x, origin.y + size.height), dst_uvs[2]}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + Command cmd; + cmd.label = "Foreground PorterDuff Blend Filter"; + cmd.BindVertices(vtx_buffer); + cmd.stencil_reference = entity.GetStencilDepth(); + auto options = OptionsFromPass(pass); + cmd.pipeline = renderer.GetPorterDuffBlendPipeline(options); + + FS::FragInfo frag_info; + VS::FrameInfo frame_info; + + auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor; + if (renderer.GetDeviceCapabilities().SupportsDecalTileMode()) { + dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; + dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; + } + auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( + dst_sampler_descriptor); + FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler); + frame_info.texture_sampler_y_coord_scale = + dst_snapshot->texture->GetYCoordScale(); + + frag_info.color = foreground_color.Premultiply(); + frag_info.input_alpha = + absorb_opacity ? dst_snapshot->opacity * alpha.value_or(1.0) : 1.0; + + auto blend_coefficients = + kPorterDuffCoefficients[static_cast(blend_mode)]; + frag_info.src_coeff = blend_coefficients[0]; + frag_info.src_coeff_dst_alpha = blend_coefficients[1]; + frag_info.dst_coeff = blend_coefficients[2]; + frag_info.dst_coeff_src_alpha = blend_coefficients[3]; + frag_info.dst_coeff_src_color = blend_coefficients[4]; + + FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info)); + + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + VS::BindFrameInfo(cmd, uniform_view); + + return pass.AddCommand(cmd); + }; + + CoverageProc coverage_proc = + [coverage](const Entity& entity) -> std::optional { + return coverage; + }; + + auto contents = AnonymousContents::Make(render_proc, coverage_proc); + Entity sub_entity; sub_entity.SetContents(std::move(contents)); sub_entity.SetStencilDepth(entity.GetStencilDepth()); @@ -531,17 +655,23 @@ std::optional BlendFilterContents::RenderFilter( } if (blend_mode_ <= Entity::kLastPipelineBlendMode) { + if (inputs.size() == 1 && foreground_color_.has_value() && + GetAbsorbOpacity()) { + return CreateForegroundPorterDuffBlend( + inputs[0], renderer, entity, coverage, foreground_color_.value(), + blend_mode_, GetAlpha(), GetAbsorbOpacity()); + } return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_, foreground_color_, GetAbsorbOpacity(), GetAlpha()); } if (blend_mode_ <= Entity::kLastAdvancedBlendMode) { - if (inputs.size() == 1 && foreground_color_.has_value()) { - return CreateForegroundBlend(inputs[0], renderer, entity, coverage, - foreground_color_.value(), blend_mode_, - GetAlpha(), GetAbsorbOpacity()); + if (inputs.size() == 1 && foreground_color_.has_value() && + GetAbsorbOpacity()) { + return CreateForegroundAdvancedBlend( + inputs[0], renderer, entity, coverage, foreground_color_.value(), + blend_mode_, GetAlpha(), GetAbsorbOpacity()); } - return advanced_blend_proc_(inputs, renderer, entity, coverage, foreground_color_, GetAbsorbOpacity(), GetAlpha()); diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h index 30bb6d401eb1f..099880db47baf 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.h +++ b/impeller/entity/contents/filters/blend_filter_contents.h @@ -42,7 +42,21 @@ class BlendFilterContents : public ColorFilterContents { /// only a single input and a foreground color. /// /// These contents cannot absorb opacity. - std::optional CreateForegroundBlend( + std::optional CreateForegroundAdvancedBlend( + const std::shared_ptr& input, + const ContentContext& renderer, + const Entity& entity, + const Rect& coverage, + Color foreground_color, + BlendMode blend_mode, + std::optional alpha, + bool absorb_opacity) const; + + /// @brief Optimized porter-duff blend that avoids a second subpass when there + /// is only a single input and a foreground color. + /// + /// These contents cannot absorb opacity. + std::optional CreateForegroundPorterDuffBlend( const std::shared_ptr& input, const ContentContext& renderer, const Entity& entity, diff --git a/impeller/entity/contents/tiled_texture_contents.cc b/impeller/entity/contents/tiled_texture_contents.cc index a53af0d0b82de..ef3fa555b558c 100644 --- a/impeller/entity/contents/tiled_texture_contents.cc +++ b/impeller/entity/contents/tiled_texture_contents.cc @@ -88,8 +88,8 @@ SamplerDescriptor TiledTextureContents::CreateDescriptor( bool TiledTextureContents::UsesEmulatedTileMode( const Capabilities& capabilities) const { - return TileModeToAddressMode(x_tile_mode_, capabilities).has_value() && - TileModeToAddressMode(y_tile_mode_, capabilities).has_value(); + return !TileModeToAddressMode(x_tile_mode_, capabilities).has_value() || + !TileModeToAddressMode(y_tile_mode_, capabilities).has_value(); } bool TiledTextureContents::Render(const ContentContext& renderer, @@ -121,7 +121,7 @@ bool TiledTextureContents::Render(const ContentContext& renderer, frame_info.texture_sampler_y_coord_scale = texture_->GetYCoordScale(); Command cmd; - cmd.label = "TiledTextureFill"; + cmd.label = uses_emulated_tile_mode ? "TiledTextureFill" : "TextureFill"; cmd.stencil_reference = entity.GetStencilDepth(); auto options = OptionsFromPassAndEntity(pass, entity); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 5353417ea5095..f1285c3393ef7 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2467,6 +2467,70 @@ TEST_P(EntityTest, ColorFilterWithForegroundColorAdvancedBlend) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } +TEST_P(EntityTest, ColorFilterWithForegroundColorClearBlend) { + auto image = CreateTextureForFixture("boston.jpg"); + auto filter = ColorFilterContents::MakeBlend( + BlendMode::kClear, FilterInput::Make({image}), Color::Red()); + + auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { + Entity entity; + entity.SetTransformation(Matrix::MakeScale(GetContentScale()) * + Matrix::MakeTranslation({500, 300}) * + Matrix::MakeScale(Vector2{0.5, 0.5})); + entity.SetContents(filter); + return entity.Render(context, pass); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(EntityTest, ColorFilterWithForegroundColorSrcBlend) { + auto image = CreateTextureForFixture("boston.jpg"); + auto filter = ColorFilterContents::MakeBlend( + BlendMode::kSource, FilterInput::Make({image}), Color::Red()); + + auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { + Entity entity; + entity.SetTransformation(Matrix::MakeScale(GetContentScale()) * + Matrix::MakeTranslation({500, 300}) * + Matrix::MakeScale(Vector2{0.5, 0.5})); + entity.SetContents(filter); + return entity.Render(context, pass); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(EntityTest, ColorFilterWithForegroundColorDstBlend) { + auto image = CreateTextureForFixture("boston.jpg"); + auto filter = ColorFilterContents::MakeBlend( + BlendMode::kDestination, FilterInput::Make({image}), Color::Red()); + + auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { + Entity entity; + entity.SetTransformation(Matrix::MakeScale(GetContentScale()) * + Matrix::MakeTranslation({500, 300}) * + Matrix::MakeScale(Vector2{0.5, 0.5})); + entity.SetContents(filter); + return entity.Render(context, pass); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(EntityTest, ColorFilterWithForegroundColorSrcInBlend) { + auto image = CreateTextureForFixture("boston.jpg"); + auto filter = ColorFilterContents::MakeBlend( + BlendMode::kSourceIn, FilterInput::Make({image}), Color::Red()); + + auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { + Entity entity; + entity.SetTransformation(Matrix::MakeScale(GetContentScale()) * + Matrix::MakeTranslation({500, 300}) * + Matrix::MakeScale(Vector2{0.5, 0.5})); + entity.SetContents(filter); + return entity.Render(context, pass); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + TEST_P(EntityTest, CoverageForStrokePathWithNegativeValuesInTransform) { auto arrow_head = PathBuilder{} .MoveTo({50, 120}) diff --git a/impeller/entity/shaders/blending/porter_duff_blend.frag b/impeller/entity/shaders/blending/porter_duff_blend.frag new file mode 100644 index 0000000000000..3b2818dee12ac --- /dev/null +++ b/impeller/entity/shaders/blending/porter_duff_blend.frag @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include + +uniform f16sampler2D texture_sampler_dst; + +uniform FragInfo { + float16_t src_coeff; + float16_t src_coeff_dst_alpha; + float16_t dst_coeff; + float16_t dst_coeff_src_alpha; + float16_t dst_coeff_src_color; + float16_t input_alpha; + f16vec4 color; +} +frag_info; + +in vec2 v_texture_coords; + +out f16vec4 frag_color; + +f16vec4 Sample(f16sampler2D texture_sampler, vec2 texture_coords) { +// gles 2.0 is the only backend without native decal support. +#ifdef IMPELLER_TARGET_OPENGLES + return IPSampleDecal(texture_sampler, texture_coords); +#else + return texture(texture_sampler, texture_coords); +#endif +} + +void main() { + f16vec4 dst = + texture(texture_sampler_dst, v_texture_coords) * frag_info.input_alpha; + f16vec4 src = frag_info.color; + frag_color = + src * (frag_info.src_coeff + dst.a * frag_info.src_coeff_dst_alpha) + + dst * (frag_info.dst_coeff + src.a * frag_info.dst_coeff_src_alpha + + src * frag_info.dst_coeff_src_color); +} diff --git a/impeller/tools/malioc.json b/impeller/tools/malioc.json index 08e437e9f02aa..1a8ea2116e782 100644 --- a/impeller/tools/malioc.json +++ b/impeller/tools/malioc.json @@ -8442,6 +8442,124 @@ } } }, + "flutter/impeller/entity/gles/porter_duff_blend.frag.gles": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/gles/porter_duff_blend.frag.gles", + "has_side_effects": false, + "has_uniform_computation": true, + "modifies_coverage": false, + "reads_color_buffer": false, + "type": "Fragment", + "uses_late_zs_test": false, + "uses_late_zs_update": false, + "variants": { + "Main": { + "fp16_arithmetic": 100, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "varying", + "texture" + ], + "longest_path_cycles": [ + 0.09375, + 0.09375, + 0.03125, + 0.0, + 0.0, + 0.25, + 0.25 + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "varying", + "texture" + ], + "shortest_path_bound_pipelines": [ + "varying", + "texture" + ], + "shortest_path_cycles": [ + 0.09375, + 0.09375, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25 + ], + "total_bound_pipelines": [ + "varying", + "texture" + ], + "total_cycles": [ + 0.09375, + 0.09375, + 0.03125, + 0.0, + 0.0, + 0.25, + 0.25 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 10, + "work_registers_used": 19 + } + } + }, + "Mali-T880": { + "core": "Mali-T880", + "filename": "flutter/impeller/entity/gles/porter_duff_blend.frag.gles", + "has_uniform_computation": false, + "type": "Fragment", + "variants": { + "Main": { + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "arithmetic" + ], + "longest_path_cycles": [ + 1.649999976158142, + 1.0, + 1.0 + ], + "pipelines": [ + "arithmetic", + "load_store", + "texture" + ], + "shortest_path_bound_pipelines": [ + "arithmetic" + ], + "shortest_path_cycles": [ + 1.649999976158142, + 1.0, + 1.0 + ], + "total_bound_pipelines": [ + "arithmetic" + ], + "total_cycles": [ + 2.0, + 1.0, + 1.0 + ] + }, + "thread_occupancy": 100, + "uniform_registers_used": 2, + "work_registers_used": 2 + } + } + } + }, "flutter/impeller/entity/gles/position_color.vert.gles": { "Mali-G78": { "core": "Mali-G78", @@ -11857,6 +11975,79 @@ } } }, + "flutter/impeller/entity/porter_duff_blend.frag.vkspv": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/porter_duff_blend.frag.vkspv", + "has_side_effects": false, + "has_uniform_computation": true, + "modifies_coverage": false, + "reads_color_buffer": false, + "type": "Fragment", + "uses_late_zs_test": false, + "uses_late_zs_update": false, + "variants": { + "Main": { + "fp16_arithmetic": 55, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "varying", + "texture" + ], + "longest_path_cycles": [ + 0.140625, + 0.140625, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25 + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "varying", + "texture" + ], + "shortest_path_bound_pipelines": [ + "varying", + "texture" + ], + "shortest_path_cycles": [ + 0.140625, + 0.140625, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25 + ], + "total_bound_pipelines": [ + "varying", + "texture" + ], + "total_cycles": [ + 0.140625, + 0.140625, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 14, + "work_registers_used": 10 + } + } + } + }, "flutter/impeller/entity/position_color.vert.vkspv": { "Mali-G78": { "core": "Mali-G78",