From 1e2583eff25200b10f01d1fc2d13e7936fba57d2 Mon Sep 17 00:00:00 2001 From: Jonah Williams <jonahwilliams@google.com> Date: Mon, 24 Feb 2025 11:01:55 -0800 Subject: [PATCH] [ui] Fix ImageFilter.shader equality to consider uniform values. (#163348) Fixes https://github.com/flutter/flutter/issues/163302 Framework widgets check for ImageFIlter.== to determine whether to mark themselves dirty. The filter obejct needs to delegate its equality to the underlying native filter so that uniform values are considered. --- engine/src/flutter/lib/ui/dart_ui.cc | 1 + engine/src/flutter/lib/ui/painting.dart | 7 ++++- .../flutter/lib/ui/painting/image_filter.cc | 4 +++ .../flutter/lib/ui/painting/image_filter.h | 1 + .../testing/dart/fragment_shader_test.dart | 28 +++++++++++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/lib/ui/dart_ui.cc b/engine/src/flutter/lib/ui/dart_ui.cc index a47bf094c42..7bb1aef0ede 100644 --- a/engine/src/flutter/lib/ui/dart_ui.cc +++ b/engine/src/flutter/lib/ui/dart_ui.cc @@ -207,6 +207,7 @@ typedef CanvasPath Path; V(ImageFilter, initComposeFilter) \ V(ImageFilter, initShader) \ V(ImageFilter, initMatrix) \ + V(ImageFilter, equals) \ V(ImageShader, dispose) \ V(ImageShader, initWithImage) \ V(ImmutableBuffer, dispose) \ diff --git a/engine/src/flutter/lib/ui/painting.dart b/engine/src/flutter/lib/ui/painting.dart index dea0c7885fa..ddda85d2752 100644 --- a/engine/src/flutter/lib/ui/painting.dart +++ b/engine/src/flutter/lib/ui/painting.dart @@ -4474,9 +4474,14 @@ class _FragmentShaderImageFilter implements ImageFilter { if (other.runtimeType != runtimeType) { return false; } - return other is _FragmentShaderImageFilter && other.shader == shader; + return other is _FragmentShaderImageFilter && + other.shader == shader && + _equals(nativeFilter, other.nativeFilter); } + @Native<Bool Function(Handle, Handle)>(symbol: 'ImageFilter::equal') + external static bool _equals(_ImageFilter a, _ImageFilter b); + @override int get hashCode => shader.hashCode; } diff --git a/engine/src/flutter/lib/ui/painting/image_filter.cc b/engine/src/flutter/lib/ui/painting/image_filter.cc index 2319008bc4f..8d21d638847 100644 --- a/engine/src/flutter/lib/ui/painting/image_filter.cc +++ b/engine/src/flutter/lib/ui/painting/image_filter.cc @@ -125,4 +125,8 @@ void ImageFilter::initShader(ReusableFragmentShader* shader) { filter_ = shader->as_image_filter(); } +bool ImageFilter::equals(ImageFilter* a, ImageFilter* b) { + return a->filter_ == b->filter_; +} + } // namespace flutter diff --git a/engine/src/flutter/lib/ui/painting/image_filter.h b/engine/src/flutter/lib/ui/painting/image_filter.h index c7bb6369bdb..c251d9653f6 100644 --- a/engine/src/flutter/lib/ui/painting/image_filter.h +++ b/engine/src/flutter/lib/ui/painting/image_filter.h @@ -36,6 +36,7 @@ class ImageFilter : public RefCountedDartWrappable<ImageFilter> { void initColorFilter(ColorFilter* colorFilter); void initComposeFilter(ImageFilter* outer, ImageFilter* inner); void initShader(ReusableFragmentShader* shader); + bool equals(ImageFilter* a, ImageFilter* b); const std::shared_ptr<DlImageFilter> filter(DlTileMode mode) const; diff --git a/engine/src/flutter/testing/dart/fragment_shader_test.dart b/engine/src/flutter/testing/dart/fragment_shader_test.dart index 0bfffbf515d..54366f7af2a 100644 --- a/engine/src/flutter/testing/dart/fragment_shader_test.dart +++ b/engine/src/flutter/testing/dart/fragment_shader_test.dart @@ -415,6 +415,34 @@ void main() async { expect(color, const Color(0xFF00FF00)); }); + // For an explaination of the problem see https://github.com/flutter/flutter/issues/163302 . + test('ImageFilter.shader equality checks consider uniform values', () async { + if (!impellerEnabled) { + print('Skipped for Skia'); + return; + } + final FragmentProgram program = await FragmentProgram.fromAsset('filter_shader.frag.iplr'); + final FragmentShader shader = program.fragmentShader(); + final ImageFilter filter = ImageFilter.shader(shader); + + // The same shader is equal to itself. + expect(filter, filter); + expect(identical(filter, filter), true); + + final ImageFilter filter_2 = ImageFilter.shader(shader); + + // The different shader is equal as long as uniforms are identical. + expect(filter, filter_2); + expect(identical(filter, filter_2), false); + + // Not equal if uniforms change. + shader.setFloat(0, 1); + final ImageFilter filter_3 = ImageFilter.shader(shader); + + expect(filter, isNot(filter_3)); + expect(identical(filter, filter_3), false); + }); + if (impellerEnabled) { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; -- GitLab