diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 9a1de1da99d92..8485580423b23 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -5231,6 +5231,9 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur.gls ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_decal.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_nodecal.frag + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel.glsl + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel_decal.frag + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel_nodecal.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/geometry/points.comp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/geometry/uv.comp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/glyph_atlas.frag + ../../../flutter/LICENSE @@ -8041,6 +8044,9 @@ FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur.glsl FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur.vert FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_decal.frag FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_nodecal.frag +FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel.glsl +FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel_decal.frag +FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel_nodecal.frag FILE: ../../../flutter/impeller/entity/shaders/geometry/points.comp FILE: ../../../flutter/impeller/entity/shaders/geometry/uv.comp FILE: ../../../flutter/impeller/entity/shaders/glyph_atlas.frag diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index f324e6a1fa133..4dd5d9fe19eb3 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -28,6 +28,8 @@ impeller_shaders("entity_shaders") { "shaders/gaussian_blur/gaussian_blur.vert", "shaders/gaussian_blur/gaussian_blur_noalpha_decal.frag", "shaders/gaussian_blur/gaussian_blur_noalpha_nodecal.frag", + "shaders/gaussian_blur/kernel_decal.frag", + "shaders/gaussian_blur/kernel_nodecal.frag", "shaders/glyph_atlas.frag", "shaders/glyph_atlas_color.frag", "shaders/glyph_atlas.vert", @@ -245,6 +247,7 @@ impeller_component("entity") { } deps = [ "//flutter/fml" ] + defines = [ "_USE_MATH_DEFINES" ] } impeller_component("entity_test_helpers") { diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 886bc54e95225..606420b0ece98 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -336,6 +336,8 @@ ContentContext::ContentContext( options_trianglestrip); gaussian_blur_noalpha_nodecal_pipelines_.CreateDefault(*context_, options_trianglestrip); + kernel_decal_pipelines_.CreateDefault(*context_, options_trianglestrip); + kernel_nodecal_pipelines_.CreateDefault(*context_, options_trianglestrip); border_mask_blur_pipelines_.CreateDefault(*context_, options_trianglestrip); morphology_filter_pipelines_.CreateDefault(*context_, options_trianglestrip, {supports_decal}); diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 98dc447f009fb..d8217da6d5e35 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -67,6 +67,8 @@ #include "impeller/entity/gaussian_blur.vert.h" #include "impeller/entity/gaussian_blur_noalpha_decal.frag.h" #include "impeller/entity/gaussian_blur_noalpha_nodecal.frag.h" +#include "impeller/entity/kernel_decal.frag.h" +#include "impeller/entity/kernel_nodecal.frag.h" #include "impeller/entity/position_color.vert.h" @@ -137,6 +139,10 @@ using GaussianBlurDecalPipeline = using GaussianBlurPipeline = RenderPipelineT; +using KernelDecalPipeline = + RenderPipelineT; +using KernelPipeline = + RenderPipelineT; using BorderMaskBlurPipeline = RenderPipelineT; using MorphologyFilterPipeline = @@ -447,6 +453,16 @@ class ContentContext { return GetPipeline(gaussian_blur_noalpha_nodecal_pipelines_, opts); } + std::shared_ptr> GetKernelDecalPipeline( + ContentContextOptions opts) const { + return GetPipeline(kernel_decal_pipelines_, opts); + } + + std::shared_ptr> GetKernelPipeline( + ContentContextOptions opts) const { + return GetPipeline(kernel_nodecal_pipelines_, opts); + } + std::shared_ptr> GetBorderMaskBlurPipeline( ContentContextOptions opts) const { return GetPipeline(border_mask_blur_pipelines_, opts); @@ -813,6 +829,8 @@ class ContentContext { gaussian_blur_noalpha_decal_pipelines_; mutable Variants gaussian_blur_noalpha_nodecal_pipelines_; + mutable Variants kernel_decal_pipelines_; + mutable Variants kernel_nodecal_pipelines_; mutable Variants border_mask_blur_pipelines_; mutable Variants morphology_filter_pipelines_; mutable Variants diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 19aa2e46fa32b..22fbce32ce1ce 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -4,6 +4,8 @@ #include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" +#include + #include "impeller/entity/contents/content_context.h" #include "impeller/entity/texture_fill.frag.h" #include "impeller/entity/texture_fill.vert.h" @@ -14,8 +16,8 @@ namespace impeller { -using GaussianBlurVertexShader = GaussianBlurPipeline::VertexShader; -using GaussianBlurFragmentShader = GaussianBlurPipeline::FragmentShader; +using GaussianBlurVertexShader = KernelPipeline::VertexShader; +using GaussianBlurFragmentShader = KernelPipeline::FragmentShader; namespace { @@ -120,7 +122,7 @@ fml::StatusOr MakeBlurSubpass( const RenderTarget& input_pass, const SamplerDescriptor& sampler_descriptor, Entity::TileMode tile_mode, - const GaussianBlurFragmentShader::BlurInfo& blur_info, + const BlurParameters& blur_info, std::optional destination_target, const Quad& blur_uvs) { if (blur_info.blur_sigma < kEhCloseEnough) { @@ -147,9 +149,9 @@ fml::StatusOr MakeBlurSubpass( if (tile_mode == Entity::TileMode::kDecal && !renderer.GetDeviceCapabilities() .SupportsDecalSamplerAddressMode()) { - cmd.pipeline = renderer.GetGaussianBlurDecalPipeline(options); + cmd.pipeline = renderer.GetKernelDecalPipeline(options); } else { - cmd.pipeline = renderer.GetGaussianBlurPipeline(options); + cmd.pipeline = renderer.GetKernelPipeline(options); } BindVertices(cmd, host_buffer, @@ -169,8 +171,8 @@ fml::StatusOr MakeBlurSubpass( linear_sampler_descriptor)); GaussianBlurVertexShader::BindFrameInfo( cmd, host_buffer.EmplaceUniform(frame_info)); - GaussianBlurFragmentShader::BindBlurInfo( - cmd, host_buffer.EmplaceUniform(blur_info)); + GaussianBlurFragmentShader::BindKernelSamples( + cmd, host_buffer.EmplaceUniform(GenerateBlurInfo(blur_info))); pass.AddCommand(std::move(cmd)); return true; @@ -343,16 +345,17 @@ std::optional GaussianBlurFilterContents::RenderFilter( } } - fml::StatusOr pass2_out = - MakeBlurSubpass(renderer, /*input_pass=*/pass1_out.value(), - input_snapshot->sampler_descriptor, tile_mode_, - GaussianBlurFragmentShader::BlurInfo{ - .blur_uv_offset = Point(0.0, pass1_pixel_size.y), - .blur_sigma = scaled_sigma.y * effective_scalar.y, - .blur_radius = blur_radius.y * effective_scalar.y, - .step_size = 1.0, - }, - /*destination_target=*/std::nullopt, blur_uvs); + fml::StatusOr pass2_out = MakeBlurSubpass( + renderer, /*input_pass=*/pass1_out.value(), + input_snapshot->sampler_descriptor, tile_mode_, + BlurParameters{ + .blur_uv_offset = Point(0.0, pass1_pixel_size.y), + .blur_sigma = scaled_sigma.y * effective_scalar.y, + .blur_radius = + static_cast(std::round(blur_radius.y * effective_scalar.y)), + .step_size = 1, + }, + /*destination_target=*/std::nullopt, blur_uvs); if (!pass2_out.ok()) { return std::nullopt; @@ -364,16 +367,17 @@ std::optional GaussianBlurFilterContents::RenderFilter( ? std::optional(pass1_out.value()) : std::optional(std::nullopt); - fml::StatusOr pass3_out = - MakeBlurSubpass(renderer, /*input_pass=*/pass2_out.value(), - input_snapshot->sampler_descriptor, tile_mode_, - GaussianBlurFragmentShader::BlurInfo{ - .blur_uv_offset = Point(pass1_pixel_size.x, 0.0), - .blur_sigma = scaled_sigma.x * effective_scalar.x, - .blur_radius = blur_radius.x * effective_scalar.x, - .step_size = 1.0, - }, - pass3_destination, blur_uvs); + fml::StatusOr pass3_out = MakeBlurSubpass( + renderer, /*input_pass=*/pass2_out.value(), + input_snapshot->sampler_descriptor, tile_mode_, + BlurParameters{ + .blur_uv_offset = Point(pass1_pixel_size.x, 0.0), + .blur_sigma = scaled_sigma.x * effective_scalar.x, + .blur_radius = + static_cast(std::round(blur_radius.x * effective_scalar.x)), + .step_size = 1, + }, + pass3_destination, blur_uvs); if (!pass3_out.ok()) { return std::nullopt; @@ -429,4 +433,31 @@ Scalar GaussianBlurFilterContents::ScaleSigma(Scalar sigma) { return clamped * scalar; } +KernelPipeline::FragmentShader::KernelSamples GenerateBlurInfo( + BlurParameters parameters) { + KernelPipeline::FragmentShader::KernelSamples result; + result.sample_count = + ((2 * parameters.blur_radius) / parameters.step_size) + 1; + FML_CHECK(result.sample_count < 24); + + Scalar tally = 0.0f; + for (int i = 0; i < result.sample_count; ++i) { + int x = (i * parameters.step_size) - parameters.blur_radius; + result.samples[i] = KernelPipeline::FragmentShader::KernelSample{ + .uv_offset = parameters.blur_uv_offset * x, + .coefficient = expf(-0.5f * (x * x) / + (parameters.blur_sigma * parameters.blur_sigma)) / + (sqrtf(2.0f * M_PI) * parameters.blur_sigma), + }; + tally += result.samples[i].coefficient; + } + + // Make sure everything adds up to 1. + for (auto& sample : result.samples) { + sample.coefficient /= tally; + } + + return result; +} + } // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index a139771b3f8b1..1bb594f3e4ae6 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -6,10 +6,21 @@ #define FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_GAUSSIAN_BLUR_FILTER_CONTENTS_H_ #include +#include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/filter_contents.h" namespace impeller { +struct BlurParameters { + Point blur_uv_offset; + Scalar blur_sigma; + int blur_radius; + int step_size; +}; + +KernelPipeline::FragmentShader::KernelSamples GenerateBlurInfo( + BlurParameters parameters); + /// Performs a bidirectional Gaussian blur. /// /// This is accomplished by rendering multiple passes in multiple directions. diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc index 4350c9318b7c3..08f00f4f72efa 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc @@ -381,5 +381,30 @@ TEST(GaussianBlurFilterContentsTest, CalculateSigmaForBlurRadius) { EXPECT_NEAR(sigma, derived_sigma, 0.01f); } +TEST(GaussianBlurFilterContentsTest, Coefficients) { + BlurParameters parameters = {.blur_uv_offset = Point(1, 0), + .blur_sigma = 1, + .blur_radius = 5, + .step_size = 1}; + KernelPipeline::FragmentShader::KernelSamples samples = + GenerateBlurInfo(parameters); + EXPECT_EQ(samples.sample_count, 11); + + // Coefficients should add up to 1. + Scalar tally = 0; + for (int i = 0; i < samples.sample_count; ++i) { + tally += samples.samples[i].coefficient; + } + EXPECT_FLOAT_EQ(tally, 1.0f); + + // Verify the shape of the curve. + for (int i = 0; i < 5; ++i) { + EXPECT_FLOAT_EQ(samples.samples[i].coefficient, + samples.samples[10 - i].coefficient); + EXPECT_TRUE(samples.samples[i + 1].coefficient > + samples.samples[i].coefficient); + } +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/shaders/gaussian_blur/kernel.glsl b/impeller/entity/shaders/gaussian_blur/kernel.glsl new file mode 100644 index 0000000000000..57f0de0a61183 --- /dev/null +++ b/impeller/entity/shaders/gaussian_blur/kernel.glsl @@ -0,0 +1,46 @@ +// 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; + +struct KernelSample { + vec2 uv_offset; + float coefficient; +}; + +uniform KernelSamples { + int sample_count; + KernelSample samples[24]; +} +blur_info; + +f16vec4 Sample(f16sampler2D tex, vec2 coords) { +#if ENABLE_DECAL_SPECIALIZATION + return IPHalfSampleDecal(tex, coords); +#else + return texture(tex, coords); +#endif +} + +in vec2 v_texture_coords; + +out f16vec4 frag_color; + +void main() { + f16vec4 total_color = f16vec4(0.0hf); + + for (int i = 0; i < blur_info.sample_count; ++i) { + float16_t coefficient = float16_t(blur_info.samples[i].coefficient); + total_color += + coefficient * Sample(texture_sampler, + v_texture_coords + blur_info.samples[i].uv_offset); + } + + frag_color = total_color; +} diff --git a/impeller/entity/shaders/gaussian_blur/kernel_decal.frag b/impeller/entity/shaders/gaussian_blur/kernel_decal.frag new file mode 100644 index 0000000000000..114ee133fd068 --- /dev/null +++ b/impeller/entity/shaders/gaussian_blur/kernel_decal.frag @@ -0,0 +1,9 @@ +// 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. + +precision mediump float; + +#define ENABLE_DECAL_SPECIALIZATION 1 + +#include "kernel.glsl" diff --git a/impeller/entity/shaders/gaussian_blur/kernel_nodecal.frag b/impeller/entity/shaders/gaussian_blur/kernel_nodecal.frag new file mode 100644 index 0000000000000..ee87a79d00823 --- /dev/null +++ b/impeller/entity/shaders/gaussian_blur/kernel_nodecal.frag @@ -0,0 +1,9 @@ +// 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. + +precision mediump float; + +#define ENABLE_DECAL_SPECIALIZATION 0 + +#include "kernel.glsl" diff --git a/impeller/tools/malioc.json b/impeller/tools/malioc.json index c4f82b8a27cb2..5c7698ea0178c 100644 --- a/impeller/tools/malioc.json +++ b/impeller/tools/malioc.json @@ -4029,6 +4029,240 @@ } } }, + "flutter/impeller/entity/gles/kernel_decal.frag.gles": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/gles/kernel_decal.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": 66, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + null + ], + "longest_path_cycles": [ + null, + null, + null, + null, + null, + null, + null + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "varying", + "texture" + ], + "shortest_path_bound_pipelines": [ + "arith_total", + "arith_cvt" + ], + "shortest_path_cycles": [ + 0.078125, + 0.0, + 0.078125, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 0.359375, + 0.0625, + 0.359375, + 0.1875, + 2.0, + 0.25, + 0.25 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 4, + "work_registers_used": 19 + } + } + }, + "Mali-T880": { + "core": "Mali-T880", + "filename": "flutter/impeller/entity/gles/kernel_decal.frag.gles", + "has_uniform_computation": false, + "type": "Fragment", + "variants": { + "Main": { + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + null + ], + "longest_path_cycles": [ + null, + null, + null + ], + "pipelines": [ + "arithmetic", + "load_store", + "texture" + ], + "shortest_path_bound_pipelines": [ + "arithmetic", + "load_store" + ], + "shortest_path_cycles": [ + 1.0, + 1.0, + 0.0 + ], + "total_bound_pipelines": [ + "arithmetic" + ], + "total_cycles": [ + 3.6666667461395264, + 3.0, + 1.0 + ] + }, + "thread_occupancy": 100, + "uniform_registers_used": 1, + "work_registers_used": 3 + } + } + } + }, + "flutter/impeller/entity/gles/kernel_nodecal.frag.gles": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/gles/kernel_nodecal.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": 50, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + null + ], + "longest_path_cycles": [ + null, + null, + null, + null, + null, + null, + null + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "varying", + "texture" + ], + "shortest_path_bound_pipelines": [ + "arith_total", + "arith_cvt" + ], + "shortest_path_cycles": [ + 0.078125, + 0.0, + 0.078125, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 0.25, + 0.0625, + 0.25, + 0.0, + 2.0, + 0.25, + 0.25 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 4, + "work_registers_used": 19 + } + } + }, + "Mali-T880": { + "core": "Mali-T880", + "filename": "flutter/impeller/entity/gles/kernel_nodecal.frag.gles", + "has_uniform_computation": false, + "type": "Fragment", + "variants": { + "Main": { + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + null + ], + "longest_path_cycles": [ + null, + null, + null + ], + "pipelines": [ + "arithmetic", + "load_store", + "texture" + ], + "shortest_path_bound_pipelines": [ + "arithmetic", + "load_store" + ], + "shortest_path_cycles": [ + 1.0, + 1.0, + 0.0 + ], + "total_bound_pipelines": [ + "arithmetic" + ], + "total_cycles": [ + 2.3333332538604736, + 2.0, + 1.0 + ] + }, + "thread_occupancy": 100, + "uniform_registers_used": 1, + "work_registers_used": 3 + } + } + } + }, "flutter/impeller/entity/gles/linear_gradient_fill.frag.gles": { "Mali-G78": { "core": "Mali-G78", @@ -7720,6 +7954,148 @@ } } }, + "flutter/impeller/entity/kernel_decal.frag.vkspv": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/kernel_decal.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": 66, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + null + ], + "longest_path_cycles": [ + null, + null, + null, + null, + null, + null, + null + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "varying", + "texture" + ], + "shortest_path_bound_pipelines": [ + "arith_total", + "arith_cvt" + ], + "shortest_path_cycles": [ + 0.09375, + 0.0, + 0.09375, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 0.34375, + 0.0625, + 0.34375, + 0.1875, + 1.0, + 0.25, + 0.25 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 4, + "work_registers_used": 17 + } + } + } + }, + "flutter/impeller/entity/kernel_nodecal.frag.vkspv": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/kernel_nodecal.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": 50, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + null + ], + "longest_path_cycles": [ + null, + null, + null, + null, + null, + null, + null + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "varying", + "texture" + ], + "shortest_path_bound_pipelines": [ + "arith_total", + "arith_cvt" + ], + "shortest_path_cycles": [ + 0.09375, + 0.0, + 0.09375, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 0.21875, + 0.0625, + 0.21875, + 0.0, + 1.0, + 0.25, + 0.25 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 4, + "work_registers_used": 11 + } + } + } + }, "flutter/impeller/entity/linear_gradient_fill.frag.vkspv": { "Mali-G78": { "core": "Mali-G78",