From 95a35543c8f9d2ba3ec022e4bce9da516fc251ec Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Sun, 16 Jun 2024 13:26:33 -0700 Subject: [PATCH 01/38] first draft --- display_list/dl_builder.cc | 3 +- display_list/effects/dl_image_filter.h | 69 ++++++++++++++++++ display_list/skia/dl_sk_conversions.cc | 4 + impeller/aiks/canvas.cc | 3 + impeller/aiks/image_filter.cc | 33 +++++++++ impeller/aiks/image_filter.h | 38 ++++++++++ impeller/display_list/dl_dispatcher.cc | 38 ++++++++++ impeller/entity/BUILD.gn | 2 + .../contents/filters/filter_contents.cc | 15 ++++ .../entity/contents/filters/filter_contents.h | 8 ++ .../filters/runtime_effect_filter_contents.cc | 73 +++++++++++++++++++ .../filters/runtime_effect_filter_contents.h | 58 +++++++++++++++ .../contents/runtime_effect_contents.cc | 11 ++- impeller/runtime_stage/runtime_stage.cc | 13 +++- lib/ui/dart_ui.cc | 1 + lib/ui/painting.dart | 45 ++++++++++++ lib/ui/painting/fragment_shader.cc | 1 + lib/ui/painting/image_filter.cc | 10 +++ lib/ui/painting/image_filter.h | 2 + testing/display_list_testing.cc | 7 ++ 20 files changed, 426 insertions(+), 8 deletions(-) create mode 100644 impeller/entity/contents/filters/runtime_effect_filter_contents.cc create mode 100644 impeller/entity/contents/filters/runtime_effect_filter_contents.h diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc index 2a048c709aaf2..394b51ea8b1c0 100644 --- a/display_list/dl_builder.cc +++ b/display_list/dl_builder.cc @@ -296,7 +296,8 @@ void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) { } case DlImageFilterType::kCompose: case DlImageFilterType::kLocalMatrix: - case DlImageFilterType::kColorFilter: { + case DlImageFilterType::kColorFilter: + case DlImageFilterType::kFragmentProgram: { Push(0, filter); break; } diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h index bcb89fd69d887..ff9fde23f3d02 100644 --- a/display_list/effects/dl_image_filter.h +++ b/display_list/effects/dl_image_filter.h @@ -7,6 +7,7 @@ #include +#include "display_list/effects/dl_color_source.h" #include "flutter/display_list/dl_attributes.h" #include "flutter/display_list/dl_sampling_options.h" #include "flutter/display_list/dl_tile_mode.h" @@ -34,6 +35,7 @@ enum class DlImageFilterType { kCompose, kColorFilter, kLocalMatrix, + kFragmentProgram, }; class DlBlurImageFilter; @@ -43,6 +45,7 @@ class DlMatrixImageFilter; class DlLocalMatrixImageFilter; class DlComposeImageFilter; class DlColorFilterImageFilter; +class DlFragmentProgramImageFilter; class DlImageFilter : public DlAttribute { public: @@ -85,6 +88,12 @@ class DlImageFilter : public DlAttribute { return nullptr; } + // Return a DlComposeImageFilter pointer to this object iff it is a + // DlFragmentProgramImageFilter type of ImageFilter, otherwise return nullptr. + virtual const DlFragmentProgramImageFilter* asFragmentProgramFilter() const { + return nullptr; + } + // Return a boolean indicating whether the image filtering operation will // modify transparent black. This is typically used to determine if applying // the ImageFilter to a temporary saveLayer buffer will turn the surrounding @@ -741,6 +750,66 @@ class DlLocalMatrixImageFilter final : public DlImageFilter { std::shared_ptr image_filter_; }; +class DlFragmentProgramImageFilter final : public DlImageFilter { + public: + explicit DlFragmentProgramImageFilter( + std::shared_ptr runtime_effect) + : runtime_effect_(std::move(runtime_effect)) { + FML_DCHECK(!!runtime_effect_->asRuntimeEffect()); + } + + std::shared_ptr shared() const override { + return std::make_shared(this->runtime_effect_); + } + + static std::shared_ptr Make( + std::shared_ptr runtime_effect) { + return std::make_shared( + std::move(runtime_effect)); + } + + DlImageFilterType type() const override { + return DlImageFilterType::kFragmentProgram; + } + size_t size() const override { return sizeof(*this); } + + bool modifies_transparent_black() const override { return false; } + + const std::shared_ptr& GetRuntimeEffect() const { + return runtime_effect_; + } + + SkRect* map_local_bounds(const SkRect& input_bounds, + SkRect& output_bounds) const override { + output_bounds = input_bounds; + return &output_bounds; + } + + SkIRect* map_device_bounds(const SkIRect& input_bounds, + const SkMatrix& ctm, + SkIRect& output_bounds) const override { + output_bounds = input_bounds; + return &output_bounds; + } + + SkIRect* get_input_device_bounds(const SkIRect& output_bounds, + const SkMatrix& ctm, + SkIRect& input_bounds) const override { + input_bounds = output_bounds; + return &input_bounds; + } + + const DlFragmentProgramImageFilter* asFragmentProgramFilter() const override { + return this; + } + + protected: + bool equals_(const DlImageFilter& other) const override { return false; } + + private: + std::shared_ptr runtime_effect_; +}; + } // namespace flutter #endif // FLUTTER_DISPLAY_LIST_EFFECTS_DL_IMAGE_FILTER_H_ diff --git a/display_list/skia/dl_sk_conversions.cc b/display_list/skia/dl_sk_conversions.cc index ca30d2ad6f91e..8fcc6b60aa36e 100644 --- a/display_list/skia/dl_sk_conversions.cc +++ b/display_list/skia/dl_sk_conversions.cc @@ -230,6 +230,10 @@ sk_sp ToSk(const DlImageFilter* filter) { } return skia_filter->makeWithLocalMatrix(lm_filter->matrix()); } + case DlImageFilterType::kFragmentProgram: + // UNSUPPORTED. + return nullptr; + break; } } diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index f396c42e25ba4..0f5c3ac026ed7 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -209,6 +209,9 @@ class MipCountVisitor : public ImageFilterVisitor { virtual void Visit(const ColorImageFilter& filter) { required_mip_count_ = 1; } + virtual void Visit(const RuntimeEffectImageFilter& filter) { + required_mip_count_ = 1; + } int32_t GetRequiredMipCount() const { return required_mip_count_; } private: diff --git a/impeller/aiks/image_filter.cc b/impeller/aiks/image_filter.cc index 10e0d72cef676..dd91c38fa8f4e 100644 --- a/impeller/aiks/image_filter.cc +++ b/impeller/aiks/image_filter.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "impeller/aiks/image_filter.h" +#include #include "impeller/entity/contents/filters/color_filter_contents.h" #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" @@ -64,6 +65,14 @@ std::shared_ptr ImageFilter::GetFilterContents() const { return WrapInput(FilterInput::Make(Rect())); } +std::shared_ptr ImageFilter::MakeRuntimeEffect( + std::shared_ptr runtime_stage, + std::shared_ptr> uniforms, + std::vector texture_inputs) { + return std::make_shared( + std::move(runtime_stage), std::move(uniforms), std::move(texture_inputs)); +} + /******************************************************************************* ******* BlurImageFilter ******************************************************************************/ @@ -205,4 +214,28 @@ std::shared_ptr LocalMatrixImageFilter::Clone() const { return std::make_shared(*this); } +/******************************************************************************* + ******* RuntimeEffectImageFilter + ******************************************************************************/ + +RuntimeEffectImageFilter::RuntimeEffectImageFilter( + std::shared_ptr runtime_stage, + std::shared_ptr> uniforms, + std::vector texture_inputs) + : runtime_stage_(std::move(runtime_stage)), + uniforms_(std::move(uniforms)), + texture_inputs_(std::move(texture_inputs)) {} + +// |ImageFilter| +std::shared_ptr RuntimeEffectImageFilter::WrapInput( + const FilterInput::Ref& input) const { + return FilterContents::MakeRuntimeEffect(input, runtime_stage_, uniforms_, + texture_inputs_); +} + +// |ImageFilter| +std::shared_ptr RuntimeEffectImageFilter::Clone() const { + return std::make_shared(*this); +} + } // namespace impeller diff --git a/impeller/aiks/image_filter.h b/impeller/aiks/image_filter.h index 9d72c14d1a522..aa6f9a49aacad 100644 --- a/impeller/aiks/image_filter.h +++ b/impeller/aiks/image_filter.h @@ -8,9 +8,11 @@ #include "impeller/aiks/color_filter.h" #include "impeller/core/sampler_descriptor.h" #include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/contents/runtime_effect_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/sigma.h" +#include "impeller/runtime_stage/runtime_stage.h" namespace impeller { @@ -23,6 +25,7 @@ class ErodeImageFilter; class MatrixImageFilter; class ComposeImageFilter; class ColorImageFilter; +class RuntimeEffectImageFilter; class ImageFilterVisitor { public: @@ -33,6 +36,7 @@ class ImageFilterVisitor { virtual void Visit(const MatrixImageFilter& filter) = 0; virtual void Visit(const ComposeImageFilter& filter) = 0; virtual void Visit(const ColorImageFilter& filter) = 0; + virtual void Visit(const RuntimeEffectImageFilter& filter) = 0; }; /******************************************************************************* @@ -71,6 +75,11 @@ class ImageFilter { const Matrix& matrix, const ImageFilter& internal_filter); + static std::shared_ptr MakeRuntimeEffect( + std::shared_ptr runtime_stage, + std::shared_ptr> uniforms, + std::vector texture_inputs); + /// @brief Generate a new FilterContents using this filter's configuration. /// /// This is the same as WrapInput, except no input is set. The input @@ -271,6 +280,35 @@ class LocalMatrixImageFilter : public ImageFilter { std::shared_ptr internal_filter_; }; +/******************************************************************************* + ******* RuntimeEffectImageFilter + ******************************************************************************/ + +class RuntimeEffectImageFilter : public ImageFilter { + public: + RuntimeEffectImageFilter( + std::shared_ptr runtime_stage, + std::shared_ptr> uniforms, + std::vector texture_inputs); + + ~RuntimeEffectImageFilter() = default; + + // |ImageFilter| + std::shared_ptr WrapInput( + const FilterInput::Ref& input) const override; + + // |ImageFilter| + std::shared_ptr Clone() const override; + + // |ImageFilter| + void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + + private: + std::shared_ptr runtime_stage_; + std::shared_ptr> uniforms_; + std::vector texture_inputs_; +}; + } // namespace impeller #endif // FLUTTER_IMPELLER_AIKS_IMAGE_FILTER_H_ diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 5c5e30b53a383..10c3e70ed691d 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -11,6 +11,7 @@ #include #include +#include "display_list/dl_sampling_options.h" #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" #include "impeller/aiks/color_filter.h" @@ -604,6 +605,43 @@ static std::shared_ptr ToImageFilter( auto matrix = ToMatrix(local_matrix_filter->matrix()); return ImageFilter::MakeLocalMatrix(matrix, *image_filter); } + case flutter::DlImageFilterType::kFragmentProgram: + auto fragment_program_filter = filter->asFragmentProgramFilter(); + FML_DCHECK(fragment_program_filter); + const flutter::DlRuntimeEffectColorSource* runtime_effect_color_source = + fragment_program_filter->GetRuntimeEffect()->asRuntimeEffect(); + auto runtime_stage = + runtime_effect_color_source->runtime_effect()->runtime_stage(); + auto uniform_data = runtime_effect_color_source->uniform_data(); + auto samplers = runtime_effect_color_source->samplers(); + + std::vector texture_inputs; + size_t index = 0; + for (auto& sampler : samplers) { + if (index == 0 && sampler == nullptr) { + // Insert placeholder for filter. + texture_inputs.push_back({.sampler_descriptor = ToSamplerDescriptor( + flutter::DlFilterMode::kNearest), + .texture = nullptr}); + continue; + } + if (sampler == nullptr) { + return nullptr; + } + auto* image = sampler->asImage(); + if (!sampler->asImage()) { + UNIMPLEMENTED; + return nullptr; + } + FML_DCHECK(image->image()->impeller_texture()); + index++; + texture_inputs.push_back({ + .sampler_descriptor = ToSamplerDescriptor(image->sampling()), + .texture = image->image()->impeller_texture(), + }); + } + return ImageFilter::MakeRuntimeEffect(runtime_stage, uniform_data, + texture_inputs); } } diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index d4363be8d7804..de33d4684c601 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -133,6 +133,8 @@ impeller_component("entity") { "contents/filters/linear_to_srgb_filter_contents.h", "contents/filters/local_matrix_filter_contents.cc", "contents/filters/local_matrix_filter_contents.h", + "contents/filters/runtime_effect_filter_contents.cc", + "contents/filters/runtime_effect_filter_contents.h", "contents/filters/matrix_filter_contents.cc", "contents/filters/matrix_filter_contents.h", "contents/filters/morphology_filter_contents.cc", diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 46cfab8c874d9..60be0f8f3ad25 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -21,12 +21,14 @@ #include "impeller/entity/contents/filters/local_matrix_filter_contents.h" #include "impeller/entity/contents/filters/matrix_filter_contents.h" #include "impeller/entity/contents/filters/morphology_filter_contents.h" +#include "impeller/entity/contents/filters/runtime_effect_filter_contents.h" #include "impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h" #include "impeller/entity/contents/texture_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/render_pass.h" +#include "impeller/runtime_stage/runtime_stage.h" namespace impeller { @@ -114,6 +116,19 @@ std::shared_ptr FilterContents::MakeYUVToRGBFilter( return filter; } +std::shared_ptr FilterContents::MakeRuntimeEffect( + FilterInput::Ref input, + std::shared_ptr runtime_stage, + std::shared_ptr> uniforms, + std::vector texture_inputs) { + auto filter = std::make_shared(); + filter->SetInputs({std::move(input)}); + filter->SetRuntimeStage(std::move(runtime_stage)); + filter->SetUniforms(std::move(uniforms)); + filter->SetTextureInputs(std::move(texture_inputs)); + return filter; +} + FilterContents::FilterContents() = default; FilterContents::~FilterContents() = default; diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 27ecd6c520ed2..5c6c952e69ab6 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -12,10 +12,12 @@ #include "impeller/core/formats.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" +#include "impeller/entity/contents/runtime_effect_contents.h" #include "impeller/entity/entity.h" #include "impeller/entity/geometry/geometry.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/sigma.h" +#include "impeller/runtime_stage/runtime_stage.h" namespace impeller { @@ -77,6 +79,12 @@ class FilterContents : public Contents { std::shared_ptr uv_texture, YUVColorSpace yuv_color_space); + static std::shared_ptr MakeRuntimeEffect( + FilterInput::Ref input, + std::shared_ptr runtime_stage, + std::shared_ptr> uniforms, + std::vector texture_inputs); + FilterContents(); ~FilterContents() override; diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc new file mode 100644 index 0000000000000..e9e0ad2557669 --- /dev/null +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc @@ -0,0 +1,73 @@ +// 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 "impeller/entity/contents/filters/runtime_effect_filter_contents.h" +#include +#include "impeller/entity/contents/anonymous_contents.h" +#include "impeller/entity/contents/runtime_effect_contents.h" +#include "impeller/entity/geometry/geometry.h" +#include "impeller/geometry/point.h" +#include "impeller/geometry/size.h" + +namespace impeller { + +// |FilterContents| +std::optional RuntimeEffectFilterContents::RenderFilter( + const FilterInput::Vector& inputs, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const { + if (inputs.empty()) { + return std::nullopt; + } + + auto input_snapshot = + inputs[0]->GetSnapshot("RuntimeEffectContents", renderer, entity); + if (!input_snapshot.has_value()) { + return std::nullopt; + } + texture_inputs_[0].texture = input_snapshot->texture; +// ISize size = input_snapshot->texture->GetSize(); + // memcpy(uniforms_->data(), &size, sizeof(ISize)); + + //---------------------------------------------------------------------------- + /// Create AnonymousContents for rendering. + /// + RenderProc render_proc = [input_snapshot, runtime_stage = runtime_stage_, + uniforms = uniforms_, + texture_inputs = texture_inputs_]( + const ContentContext& renderer, + const Entity& entity, RenderPass& pass) -> bool { + RuntimeEffectContents contents; + contents.SetRuntimeStage(runtime_stage); + contents.SetUniformData(uniforms); + contents.SetTextureInputs(texture_inputs); + contents.SetGeometry( + Geometry::MakeRect(input_snapshot->GetCoverage().value())); + return contents.Render(renderer, entity, pass); + }; + + CoverageProc coverage_proc = + [coverage](const Entity& entity) -> std::optional { + return coverage.TransformBounds(entity.GetTransform()); + }; + + auto contents = AnonymousContents::Make(render_proc, coverage_proc); + + Entity sub_entity; + sub_entity.SetContents(std::move(contents)); + sub_entity.SetBlendMode(entity.GetBlendMode()); + return sub_entity; +} + +// |FilterContents| +std::optional RuntimeEffectFilterContents::GetFilterSourceCoverage( + const Matrix& effect_transform, + const Rect& output_limit) const { + return output_limit; +} + +} // namespace impeller diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.h b/impeller/entity/contents/filters/runtime_effect_filter_contents.h new file mode 100644 index 0000000000000..c6a737e0e9c55 --- /dev/null +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.h @@ -0,0 +1,58 @@ +// 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. + +#ifndef FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_RUNTIME_EFFECT_FILTER_CONTENTS_H_ +#define FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_RUNTIME_EFFECT_FILTER_CONTENTS_H_ + +#include "impeller/entity/contents/filters/filter_contents.h" + +namespace impeller { + +class RuntimeEffectFilterContents final : public FilterContents { + public: + RuntimeEffectFilterContents() {} + + ~RuntimeEffectFilterContents() {} + + void SetRuntimeStage(std::shared_ptr runtime_stage) { + runtime_stage_ = std::move(runtime_stage); + } + + void SetUniforms(std::shared_ptr> uniforms) { + uniforms_ = std::move(uniforms); + } + + void SetTextureInputs( + std::vector texture_inputs) { + texture_inputs_ = std::move(texture_inputs); + } + + private: + std::shared_ptr runtime_stage_; + mutable std::shared_ptr> uniforms_; + mutable std::vector texture_inputs_; + + // |FilterContents| + std::optional RenderFilter( + const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + const Matrix& effect_transform, + const Rect& coverage, + const std::optional& coverage_hint) const override; + + // |FilterContents| + std::optional GetFilterSourceCoverage( + const Matrix& effect_transform, + const Rect& output_limit) const override; + + RuntimeEffectFilterContents(const RuntimeEffectFilterContents&) = delete; + + RuntimeEffectFilterContents& operator=(const RuntimeEffectFilterContents&) = + delete; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_RUNTIME_EFFECT_FILTER_CONTENTS_H_ diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc index d28f64e1f291b..194977fc2aa4b 100644 --- a/impeller/entity/contents/runtime_effect_contents.cc +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -156,6 +156,9 @@ RuntimeEffectContents::CreatePipeline(const ContentContext& renderer, std::make_shared(); vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs, VS::kInterleavedBufferLayout); + // for (auto x : runtime_stage_->GetDescriptorSetLayouts()) { + // FML_LOG(ERROR) << "layout: " << static_cast(x.descriptor_type) << ", " <(x.binding); + // } vertex_descriptor->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts); vertex_descriptor->RegisterDescriptorSetLayouts( runtime_stage_->GetDescriptorSetLayouts().data(), @@ -209,9 +212,9 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, size_t buffer_index = 0; size_t buffer_offset = 0; + size_t start_index = 64; for (const auto& uniform : runtime_stage_->GetUniforms()) { std::shared_ptr metadata = MakeShaderMetadata(uniform); - switch (uniform.type) { case kSampledImage: { // Sampler uniforms are ordered in the IPLR according to their @@ -254,7 +257,7 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, Context::BackendType::kVulkan); ShaderUniformSlot uniform_slot; uniform_slot.name = uniform.name.c_str(); - uniform_slot.binding = uniform.location; + uniform_slot.binding = start_index; // TODO(jonahwilliams): rewrite this to emplace directly into // HostBuffer. @@ -280,6 +283,7 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, pass.BindResource(ShaderStage::kFragment, DescriptorType::kUniformBuffer, uniform_slot, ShaderMetadata{}, std::move(buffer_view)); + start_index++; } } } @@ -299,13 +303,14 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, SampledImageSlot image_slot; image_slot.name = uniform.name.c_str(); - image_slot.binding = uniform.binding; + image_slot.binding = start_index; image_slot.texture_index = uniform.location - minimum_sampler_index; pass.BindResource(ShaderStage::kFragment, DescriptorType::kSampledImage, image_slot, *metadata, input.texture, sampler); sampler_index++; + start_index++; break; } default: diff --git a/impeller/runtime_stage/runtime_stage.cc b/impeller/runtime_stage/runtime_stage.cc index 9e6b3eb42cc22..1c2ca9e6177be 100644 --- a/impeller/runtime_stage/runtime_stage.cc +++ b/impeller/runtime_stage/runtime_stage.cc @@ -4,7 +4,6 @@ #include "impeller/runtime_stage/runtime_stage.h" -#include #include #include "fml/mapping.h" @@ -117,19 +116,25 @@ RuntimeStage::RuntimeStage(const fb::RuntimeStage* runtime_stage, [payload = payload_](auto, auto) {} // ); + uint32_t binding_location = 64; for (const auto& uniform : GetUniforms()) { if (uniform.type == kStruct) { descriptor_set_layouts_.push_back(DescriptorSetLayout{ - static_cast(uniform.location), + binding_location, DescriptorType::kUniformBuffer, ShaderStage::kFragment, }); - } else if (uniform.type == kSampledImage) { + binding_location++; + } + } + for (const auto& uniform : GetUniforms()) { + if (uniform.type == kSampledImage) { descriptor_set_layouts_.push_back(DescriptorSetLayout{ - static_cast(uniform.binding), + binding_location, DescriptorType::kSampledImage, ShaderStage::kFragment, }); + binding_location++; } } diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 797b54f56a4bd..9a46df5490668 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -207,6 +207,7 @@ typedef CanvasPath Path; V(ImageFilter, initErode) \ V(ImageFilter, initColorFilter) \ V(ImageFilter, initComposeFilter) \ + V(ImageFilter, initShader) \ V(ImageFilter, initMatrix) \ V(ImageShader, dispose) \ V(ImageShader, initWithImage) \ diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 1e02e5d410c51..28eca84df7c63 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -3756,6 +3756,13 @@ abstract class ImageFilter { return _ComposeImageFilter(innerFilter: inner, outerFilter: outer); } + factory ImageFilter.shader(FragmentShader shader) { + if (!_impellerEnabled) { + throw StateError('ImageFilter.shader only support with Impeller rendering engine.'); + } + return _FragmentShaderImageFilter(shader); + } + // Converts this to a native DlImageFilter. See the comments of this method in // subclasses for the exact type of DlImageFilter this method converts to. _ImageFilter _toNativeImageFilter(); @@ -3929,6 +3936,35 @@ class _ComposeImageFilter implements ImageFilter { int get hashCode => Object.hash(innerFilter, outerFilter); } +class _FragmentShaderImageFilter implements ImageFilter { + _FragmentShaderImageFilter(this.shader); + + final FragmentShader shader; + + late final _ImageFilter nativeFilter = _ImageFilter.shader(this); + + @override + _ImageFilter _toNativeImageFilter() => nativeFilter; + + @override + String get _shortDescription => 'shader'; + + @override + String toString() => 'ImageFilter.shader()'; + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) { + return false; + } + return other is _FragmentShaderImageFilter + && other.shader == shader; + } + + @override + int get hashCode => shader.hashCode; +} + /// An [ImageFilter] that is backed by a native DlImageFilter. /// /// This is a private class, rather than being the implementation of the public @@ -3988,6 +4024,12 @@ base class _ImageFilter extends NativeFieldWrapperClass1 { _initComposed(nativeFilterOuter, nativeFilterInner); } + _ImageFilter.shader(_FragmentShaderImageFilter filter) + : creator = filter { + _constructor(); + _initShader(filter.shader); + } + @Native(symbol: 'ImageFilter::Create') external void _constructor(); @@ -4009,6 +4051,9 @@ base class _ImageFilter extends NativeFieldWrapperClass1 { @Native, Pointer, Pointer)>(symbol: 'ImageFilter::initComposeFilter') external void _initComposed(_ImageFilter outerFilter, _ImageFilter innerFilter); + @Native, Pointer)>(symbol: 'ImageFilter::initShader') + external void _initShader(FragmentShader shader); + /// The original Dart object that created the native wrapper, which retains /// the values used for the filter. final ImageFilter creator; diff --git a/lib/ui/painting/fragment_shader.cc b/lib/ui/painting/fragment_shader.cc index 491d312e7e6c0..977aa02180579 100644 --- a/lib/ui/painting/fragment_shader.cc +++ b/lib/ui/painting/fragment_shader.cc @@ -101,6 +101,7 @@ std::shared_ptr ReusableFragmentShader::shader( // continually changed on the UI thread. So we take a copy of the uniforms // before handing it to the DisplayList for consumption on the render thread. auto uniform_data = std::make_shared>(); + FML_LOG(ERROR) << "Uniform data size: " << uniform_data_->size(); uniform_data->resize(uniform_data_->size()); memcpy(uniform_data->data(), uniform_data_->bytes(), uniform_data->size()); diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index 74f89834fe757..e9248a493465a 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -4,9 +4,13 @@ #include "flutter/lib/ui/painting/image_filter.h" +#include "display_list/dl_sampling_options.h" +#include "display_list/effects/dl_image_filter.h" #include "flutter/lib/ui/floating_point.h" #include "flutter/lib/ui/painting/matrix.h" #include "flutter/lib/ui/ui_dart_state.h" +#include "lib/ui/painting/fragment_program.h" +#include "lib/ui/painting/fragment_shader.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_binding_macros.h" @@ -84,4 +88,10 @@ void ImageFilter::initComposeFilter(ImageFilter* outer, ImageFilter* inner) { filter_ = DlComposeImageFilter::Make(outer->filter(), inner->filter()); } +void ImageFilter::initShader(ReusableFragmentShader* shader) { + FML_DCHECK(shader); + filter_ = DlFragmentProgramImageFilter::Make( + shader->shader(DlImageSampling::kNearestNeighbor)); +} + } // namespace flutter diff --git a/lib/ui/painting/image_filter.h b/lib/ui/painting/image_filter.h index 68d70b3f150c5..1ca29dad712ec 100644 --- a/lib/ui/painting/image_filter.h +++ b/lib/ui/painting/image_filter.h @@ -9,6 +9,7 @@ #include "flutter/display_list/effects/dl_image_filter.h" #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/color_filter.h" +#include "lib/ui/painting/fragment_shader.h" #include "third_party/tonic/typed_data/typed_list.h" namespace tonic { @@ -34,6 +35,7 @@ class ImageFilter : public RefCountedDartWrappable { void initMatrix(const tonic::Float64List& matrix4, int filter_quality_index); void initColorFilter(ColorFilter* colorFilter); void initComposeFilter(ImageFilter* outer, ImageFilter* inner); + void initShader(ReusableFragmentShader* shader); const std::shared_ptr filter() const { return filter_; } diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index 170504373425f..d7eb6a862000f 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -6,6 +6,7 @@ #include +#include "display_list/effects/dl_image_filter.h" #include "flutter/display_list/display_list.h" namespace flutter { @@ -634,6 +635,12 @@ void DisplayListStreamDispatcher::out(const DlImageFilter& filter) { startl() << ")"; break; } + case flutter::DlImageFilterType::kFragmentProgram: { + const DlFragmentProgramImageFilter* fragment = filter.asFragmentProgramFilter(); + FML_DCHECK(fragment); + os_ << "DlFragmentProgramImageFilter()"; + break; + } } } void DisplayListStreamDispatcher::out(const DlImageFilter* filter) { From a2992349e2894899c5c91c72d340a6660d9bc5e8 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 20 Jun 2024 08:03:19 -0700 Subject: [PATCH 02/38] ++ --- impeller/runtime_stage/runtime_stage.cc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/impeller/runtime_stage/runtime_stage.cc b/impeller/runtime_stage/runtime_stage.cc index a22321c42ceba..bf46e805a93eb 100644 --- a/impeller/runtime_stage/runtime_stage.cc +++ b/impeller/runtime_stage/runtime_stage.cc @@ -4,6 +4,7 @@ #include "impeller/runtime_stage/runtime_stage.h" +#include #include #include "fml/mapping.h" @@ -142,21 +143,16 @@ RuntimeStage::RuntimeStage(const fb::RuntimeStage* runtime_stage, for (const auto& uniform : GetUniforms()) { if (uniform.type == kStruct) { descriptor_set_layouts_.push_back(DescriptorSetLayout{ - binding_location, + static_cast(uniform.location), DescriptorType::kUniformBuffer, ShaderStage::kFragment, }); - binding_location++; - } - } - for (const auto& uniform : GetUniforms()) { - if (uniform.type == kSampledImage) { + } else if (uniform.type == kSampledImage) { descriptor_set_layouts_.push_back(DescriptorSetLayout{ - binding_location, + static_cast(uniform.binding), DescriptorType::kSampledImage, ShaderStage::kFragment, }); - binding_location++; } } is_valid_ = true; From ec1b1015a18d73853f5a5d5fdb87f6626a7834fa Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 20 Jun 2024 08:04:17 -0700 Subject: [PATCH 03/38] format --- display_list/effects/dl_image_filter.h | 3 ++- impeller/entity/BUILD.gn | 4 ++-- .../entity/contents/filters/runtime_effect_filter_contents.cc | 2 +- impeller/entity/contents/runtime_effect_contents.cc | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h index ff9fde23f3d02..9fd7e9cc809d2 100644 --- a/display_list/effects/dl_image_filter.h +++ b/display_list/effects/dl_image_filter.h @@ -759,7 +759,8 @@ class DlFragmentProgramImageFilter final : public DlImageFilter { } std::shared_ptr shared() const override { - return std::make_shared(this->runtime_effect_); + return std::make_shared( + this->runtime_effect_); } static std::shared_ptr Make( diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index de33d4684c601..ceab09c1ac9ee 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -133,12 +133,12 @@ impeller_component("entity") { "contents/filters/linear_to_srgb_filter_contents.h", "contents/filters/local_matrix_filter_contents.cc", "contents/filters/local_matrix_filter_contents.h", - "contents/filters/runtime_effect_filter_contents.cc", - "contents/filters/runtime_effect_filter_contents.h", "contents/filters/matrix_filter_contents.cc", "contents/filters/matrix_filter_contents.h", "contents/filters/morphology_filter_contents.cc", "contents/filters/morphology_filter_contents.h", + "contents/filters/runtime_effect_filter_contents.cc", + "contents/filters/runtime_effect_filter_contents.h", "contents/filters/srgb_to_linear_filter_contents.cc", "contents/filters/srgb_to_linear_filter_contents.h", "contents/filters/yuv_to_rgb_filter_contents.cc", diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc index e9e0ad2557669..3a6a07775eff7 100644 --- a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc @@ -30,7 +30,7 @@ std::optional RuntimeEffectFilterContents::RenderFilter( return std::nullopt; } texture_inputs_[0].texture = input_snapshot->texture; -// ISize size = input_snapshot->texture->GetSize(); + // ISize size = input_snapshot->texture->GetSize(); // memcpy(uniforms_->data(), &size, sizeof(ISize)); //---------------------------------------------------------------------------- diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc index 194977fc2aa4b..d342306a56c7f 100644 --- a/impeller/entity/contents/runtime_effect_contents.cc +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -157,7 +157,8 @@ RuntimeEffectContents::CreatePipeline(const ContentContext& renderer, vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs, VS::kInterleavedBufferLayout); // for (auto x : runtime_stage_->GetDescriptorSetLayouts()) { - // FML_LOG(ERROR) << "layout: " << static_cast(x.descriptor_type) << ", " <(x.binding); + // FML_LOG(ERROR) << "layout: " << static_cast(x.descriptor_type) << ", + // " <(x.binding); // } vertex_descriptor->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts); vertex_descriptor->RegisterDescriptorSetLayouts( From 2f9f72de3762c41dba81055a578891912458d9ff Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 20 Jun 2024 09:15:32 -0700 Subject: [PATCH 04/38] add basic doc comment. --- .../filters/runtime_effect_filter_contents.cc | 15 +- .../contents/runtime_effect_contents.cc | 12 +- lib/ui/dart_ui.cc | 337 +++++++++--------- lib/ui/painting.dart | 42 ++- lib/ui/painting/fragment_shader.cc | 28 +- lib/ui/painting/fragment_shader.h | 2 + 6 files changed, 246 insertions(+), 190 deletions(-) diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc index 3a6a07775eff7..3bb8bffa80fba 100644 --- a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc @@ -4,6 +4,7 @@ #include "impeller/entity/contents/filters/runtime_effect_filter_contents.h" #include +#include "impeller/base/validation.h" #include "impeller/entity/contents/anonymous_contents.h" #include "impeller/entity/contents/runtime_effect_contents.h" #include "impeller/entity/geometry/geometry.h" @@ -29,9 +30,19 @@ std::optional RuntimeEffectFilterContents::RenderFilter( if (!input_snapshot.has_value()) { return std::nullopt; } + // The shader is required to have at least one sampler, the first of + // which is treated as the input and a vec2 size uniform to compute the + // offsets. These are validated at the dart:ui layer, but to avoid crashes we + // check here too. + if (texture_inputs_.size() < 1 || uniforms_->size() < 16) { + VALIDATION_LOG + << "Invalid fragment shader in RuntimeEffectFilterContents. " + << "Shader must have at least one sampler and a vec2 size uniform."; + return std::nullopt; + } texture_inputs_[0].texture = input_snapshot->texture; - // ISize size = input_snapshot->texture->GetSize(); - // memcpy(uniforms_->data(), &size, sizeof(ISize)); + Size size = Size(input_snapshot->texture->GetSize()); + memcpy(uniforms_->data(), &size, sizeof(Size)); //---------------------------------------------------------------------------- /// Create AnonymousContents for rendering. diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc index d342306a56c7f..4550b2a6766ec 100644 --- a/impeller/entity/contents/runtime_effect_contents.cc +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -156,10 +156,6 @@ RuntimeEffectContents::CreatePipeline(const ContentContext& renderer, std::make_shared(); vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs, VS::kInterleavedBufferLayout); - // for (auto x : runtime_stage_->GetDescriptorSetLayouts()) { - // FML_LOG(ERROR) << "layout: " << static_cast(x.descriptor_type) << ", - // " <(x.binding); - // } vertex_descriptor->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts); vertex_descriptor->RegisterDescriptorSetLayouts( runtime_stage_->GetDescriptorSetLayouts().data(), @@ -213,7 +209,6 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, size_t buffer_index = 0; size_t buffer_offset = 0; - size_t start_index = 64; for (const auto& uniform : runtime_stage_->GetUniforms()) { std::shared_ptr metadata = MakeShaderMetadata(uniform); switch (uniform.type) { @@ -257,8 +252,7 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, FML_DCHECK(renderer.GetContext()->GetBackendType() == Context::BackendType::kVulkan); ShaderUniformSlot uniform_slot; - uniform_slot.name = uniform.name.c_str(); - uniform_slot.binding = start_index; + uniform_slot.binding = uniform.location; // TODO(jonahwilliams): rewrite this to emplace directly into // HostBuffer. @@ -284,7 +278,6 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, pass.BindResource(ShaderStage::kFragment, DescriptorType::kUniformBuffer, uniform_slot, ShaderMetadata{}, std::move(buffer_view)); - start_index++; } } } @@ -304,14 +297,13 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, SampledImageSlot image_slot; image_slot.name = uniform.name.c_str(); - image_slot.binding = start_index; + image_slot.binding = uniform.binding; image_slot.texture_index = uniform.location - minimum_sampler_index; pass.BindResource(ShaderStage::kFragment, DescriptorType::kSampledImage, image_slot, *metadata, input.texture, sampler); sampler_index++; - start_index++; break; } default: diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 9a46df5490668..a3c868885a0bd 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -139,174 +139,175 @@ typedef CanvasPath Path; // - Resolve the native function pointer associated with an @Native function. // If there is a mismatch between names or parameter count an @Native is // trying to resolve, an exception will be thrown. -#define FFI_METHOD_LIST(V) \ - V(Canvas, clipPath) \ - V(Canvas, clipRect) \ - V(Canvas, clipRRect) \ - V(Canvas, drawArc) \ - V(Canvas, drawAtlas) \ - V(Canvas, drawCircle) \ - V(Canvas, drawColor) \ - V(Canvas, drawDRRect) \ - V(Canvas, drawImage) \ - V(Canvas, drawImageNine) \ - V(Canvas, drawImageRect) \ - V(Canvas, drawLine) \ - V(Canvas, drawOval) \ - V(Canvas, drawPaint) \ - V(Canvas, drawPath) \ - V(Canvas, drawPicture) \ - V(Canvas, drawPoints) \ - V(Canvas, drawRRect) \ - V(Canvas, drawRect) \ - V(Canvas, drawShadow) \ - V(Canvas, drawVertices) \ - V(Canvas, getDestinationClipBounds) \ - V(Canvas, getLocalClipBounds) \ - V(Canvas, getSaveCount) \ - V(Canvas, getTransform) \ - V(Canvas, restore) \ - V(Canvas, restoreToCount) \ - V(Canvas, rotate) \ - V(Canvas, save) \ - V(Canvas, saveLayer) \ - V(Canvas, saveLayerWithoutBounds) \ - V(Canvas, scale) \ - V(Canvas, skew) \ - V(Canvas, transform) \ - V(Canvas, translate) \ - V(Codec, dispose) \ - V(Codec, frameCount) \ - V(Codec, getNextFrame) \ - V(Codec, repetitionCount) \ - V(ColorFilter, initLinearToSrgbGamma) \ - V(ColorFilter, initMatrix) \ - V(ColorFilter, initMode) \ - V(ColorFilter, initSrgbToLinearGamma) \ - V(EngineLayer, dispose) \ - V(FragmentProgram, initFromAsset) \ - V(ReusableFragmentShader, Dispose) \ - V(ReusableFragmentShader, SetImageSampler) \ - V(ReusableFragmentShader, ValidateSamplers) \ - V(Gradient, initLinear) \ - V(Gradient, initRadial) \ - V(Gradient, initSweep) \ - V(Gradient, initTwoPointConical) \ - V(Image, dispose) \ - V(Image, width) \ - V(Image, height) \ - V(Image, toByteData) \ - V(Image, colorSpace) \ - V(ImageDescriptor, bytesPerPixel) \ - V(ImageDescriptor, dispose) \ - V(ImageDescriptor, height) \ - V(ImageDescriptor, instantiateCodec) \ - V(ImageDescriptor, width) \ - V(ImageFilter, initBlur) \ - V(ImageFilter, initDilate) \ - V(ImageFilter, initErode) \ - V(ImageFilter, initColorFilter) \ - V(ImageFilter, initComposeFilter) \ - V(ImageFilter, initShader) \ - V(ImageFilter, initMatrix) \ - V(ImageShader, dispose) \ - V(ImageShader, initWithImage) \ - V(ImmutableBuffer, dispose) \ - V(ImmutableBuffer, length) \ - V(ParagraphBuilder, addPlaceholder) \ - V(ParagraphBuilder, addText) \ - V(ParagraphBuilder, build) \ - V(ParagraphBuilder, pop) \ - V(ParagraphBuilder, pushStyle) \ - V(Paragraph, alphabeticBaseline) \ - V(Paragraph, computeLineMetrics) \ - V(Paragraph, didExceedMaxLines) \ - V(Paragraph, dispose) \ - V(Paragraph, getClosestGlyphInfo) \ - V(Paragraph, getGlyphInfoAt) \ - V(Paragraph, getLineBoundary) \ - V(Paragraph, getLineMetricsAt) \ - V(Paragraph, getLineNumberAt) \ - V(Paragraph, getNumberOfLines) \ - V(Paragraph, getPositionForOffset) \ - V(Paragraph, getRectsForPlaceholders) \ - V(Paragraph, getRectsForRange) \ - V(Paragraph, getWordBoundary) \ - V(Paragraph, height) \ - V(Paragraph, ideographicBaseline) \ - V(Paragraph, layout) \ - V(Paragraph, longestLine) \ - V(Paragraph, maxIntrinsicWidth) \ - V(Paragraph, minIntrinsicWidth) \ - V(Paragraph, paint) \ - V(Paragraph, width) \ - V(PathMeasure, setPath) \ - V(PathMeasure, getLength) \ - V(PathMeasure, getPosTan) \ - V(PathMeasure, getSegment) \ - V(PathMeasure, isClosed) \ - V(PathMeasure, nextContour) \ - V(Path, addArc) \ - V(Path, addOval) \ - V(Path, addPath) \ - V(Path, addPathWithMatrix) \ - V(Path, addPolygon) \ - V(Path, addRRect) \ - V(Path, addRect) \ - V(Path, arcTo) \ - V(Path, arcToPoint) \ - V(Path, clone) \ - V(Path, close) \ - V(Path, conicTo) \ - V(Path, contains) \ - V(Path, cubicTo) \ - V(Path, extendWithPath) \ - V(Path, extendWithPathAndMatrix) \ - V(Path, getBounds) \ - V(Path, getFillType) \ - V(Path, lineTo) \ - V(Path, moveTo) \ - V(Path, op) \ - V(Path, quadraticBezierTo) \ - V(Path, relativeArcToPoint) \ - V(Path, relativeConicTo) \ - V(Path, relativeCubicTo) \ - V(Path, relativeLineTo) \ - V(Path, relativeMoveTo) \ - V(Path, relativeQuadraticBezierTo) \ - V(Path, reset) \ - V(Path, setFillType) \ - V(Path, shift) \ - V(Path, transform) \ - V(PictureRecorder, endRecording) \ - V(Picture, GetAllocationSize) \ - V(Picture, dispose) \ - V(Picture, toImage) \ - V(Picture, toImageSync) \ - V(SceneBuilder, addPerformanceOverlay) \ - V(SceneBuilder, addPicture) \ - V(SceneBuilder, addPlatformView) \ - V(SceneBuilder, addRetained) \ - V(SceneBuilder, addTexture) \ - V(SceneBuilder, build) \ - V(SceneBuilder, pop) \ - V(SceneBuilder, pushBackdropFilter) \ - V(SceneBuilder, pushClipPath) \ - V(SceneBuilder, pushClipRRect) \ - V(SceneBuilder, pushClipRect) \ - V(SceneBuilder, pushColorFilter) \ - V(SceneBuilder, pushImageFilter) \ - V(SceneBuilder, pushOffset) \ - V(SceneBuilder, pushOpacity) \ - V(SceneBuilder, pushShaderMask) \ - V(SceneBuilder, pushTransformHandle) \ - V(Scene, dispose) \ - V(Scene, toImage) \ - V(Scene, toImageSync) \ - V(SemanticsUpdateBuilder, build) \ - V(SemanticsUpdateBuilder, updateCustomAction) \ - V(SemanticsUpdateBuilder, updateNode) \ - V(SemanticsUpdate, dispose) \ +#define FFI_METHOD_LIST(V) \ + V(Canvas, clipPath) \ + V(Canvas, clipRect) \ + V(Canvas, clipRRect) \ + V(Canvas, drawArc) \ + V(Canvas, drawAtlas) \ + V(Canvas, drawCircle) \ + V(Canvas, drawColor) \ + V(Canvas, drawDRRect) \ + V(Canvas, drawImage) \ + V(Canvas, drawImageNine) \ + V(Canvas, drawImageRect) \ + V(Canvas, drawLine) \ + V(Canvas, drawOval) \ + V(Canvas, drawPaint) \ + V(Canvas, drawPath) \ + V(Canvas, drawPicture) \ + V(Canvas, drawPoints) \ + V(Canvas, drawRRect) \ + V(Canvas, drawRect) \ + V(Canvas, drawShadow) \ + V(Canvas, drawVertices) \ + V(Canvas, getDestinationClipBounds) \ + V(Canvas, getLocalClipBounds) \ + V(Canvas, getSaveCount) \ + V(Canvas, getTransform) \ + V(Canvas, restore) \ + V(Canvas, restoreToCount) \ + V(Canvas, rotate) \ + V(Canvas, save) \ + V(Canvas, saveLayer) \ + V(Canvas, saveLayerWithoutBounds) \ + V(Canvas, scale) \ + V(Canvas, skew) \ + V(Canvas, transform) \ + V(Canvas, translate) \ + V(Codec, dispose) \ + V(Codec, frameCount) \ + V(Codec, getNextFrame) \ + V(Codec, repetitionCount) \ + V(ColorFilter, initLinearToSrgbGamma) \ + V(ColorFilter, initMatrix) \ + V(ColorFilter, initMode) \ + V(ColorFilter, initSrgbToLinearGamma) \ + V(EngineLayer, dispose) \ + V(FragmentProgram, initFromAsset) \ + V(ReusableFragmentShader, Dispose) \ + V(ReusableFragmentShader, SetImageSampler) \ + V(ReusableFragmentShader, ValidateSamplers) \ + V(ReusableFragmentShader, ValidateImageFilter) \ + V(Gradient, initLinear) \ + V(Gradient, initRadial) \ + V(Gradient, initSweep) \ + V(Gradient, initTwoPointConical) \ + V(Image, dispose) \ + V(Image, width) \ + V(Image, height) \ + V(Image, toByteData) \ + V(Image, colorSpace) \ + V(ImageDescriptor, bytesPerPixel) \ + V(ImageDescriptor, dispose) \ + V(ImageDescriptor, height) \ + V(ImageDescriptor, instantiateCodec) \ + V(ImageDescriptor, width) \ + V(ImageFilter, initBlur) \ + V(ImageFilter, initDilate) \ + V(ImageFilter, initErode) \ + V(ImageFilter, initColorFilter) \ + V(ImageFilter, initComposeFilter) \ + V(ImageFilter, initShader) \ + V(ImageFilter, initMatrix) \ + V(ImageShader, dispose) \ + V(ImageShader, initWithImage) \ + V(ImmutableBuffer, dispose) \ + V(ImmutableBuffer, length) \ + V(ParagraphBuilder, addPlaceholder) \ + V(ParagraphBuilder, addText) \ + V(ParagraphBuilder, build) \ + V(ParagraphBuilder, pop) \ + V(ParagraphBuilder, pushStyle) \ + V(Paragraph, alphabeticBaseline) \ + V(Paragraph, computeLineMetrics) \ + V(Paragraph, didExceedMaxLines) \ + V(Paragraph, dispose) \ + V(Paragraph, getClosestGlyphInfo) \ + V(Paragraph, getGlyphInfoAt) \ + V(Paragraph, getLineBoundary) \ + V(Paragraph, getLineMetricsAt) \ + V(Paragraph, getLineNumberAt) \ + V(Paragraph, getNumberOfLines) \ + V(Paragraph, getPositionForOffset) \ + V(Paragraph, getRectsForPlaceholders) \ + V(Paragraph, getRectsForRange) \ + V(Paragraph, getWordBoundary) \ + V(Paragraph, height) \ + V(Paragraph, ideographicBaseline) \ + V(Paragraph, layout) \ + V(Paragraph, longestLine) \ + V(Paragraph, maxIntrinsicWidth) \ + V(Paragraph, minIntrinsicWidth) \ + V(Paragraph, paint) \ + V(Paragraph, width) \ + V(PathMeasure, setPath) \ + V(PathMeasure, getLength) \ + V(PathMeasure, getPosTan) \ + V(PathMeasure, getSegment) \ + V(PathMeasure, isClosed) \ + V(PathMeasure, nextContour) \ + V(Path, addArc) \ + V(Path, addOval) \ + V(Path, addPath) \ + V(Path, addPathWithMatrix) \ + V(Path, addPolygon) \ + V(Path, addRRect) \ + V(Path, addRect) \ + V(Path, arcTo) \ + V(Path, arcToPoint) \ + V(Path, clone) \ + V(Path, close) \ + V(Path, conicTo) \ + V(Path, contains) \ + V(Path, cubicTo) \ + V(Path, extendWithPath) \ + V(Path, extendWithPathAndMatrix) \ + V(Path, getBounds) \ + V(Path, getFillType) \ + V(Path, lineTo) \ + V(Path, moveTo) \ + V(Path, op) \ + V(Path, quadraticBezierTo) \ + V(Path, relativeArcToPoint) \ + V(Path, relativeConicTo) \ + V(Path, relativeCubicTo) \ + V(Path, relativeLineTo) \ + V(Path, relativeMoveTo) \ + V(Path, relativeQuadraticBezierTo) \ + V(Path, reset) \ + V(Path, setFillType) \ + V(Path, shift) \ + V(Path, transform) \ + V(PictureRecorder, endRecording) \ + V(Picture, GetAllocationSize) \ + V(Picture, dispose) \ + V(Picture, toImage) \ + V(Picture, toImageSync) \ + V(SceneBuilder, addPerformanceOverlay) \ + V(SceneBuilder, addPicture) \ + V(SceneBuilder, addPlatformView) \ + V(SceneBuilder, addRetained) \ + V(SceneBuilder, addTexture) \ + V(SceneBuilder, build) \ + V(SceneBuilder, pop) \ + V(SceneBuilder, pushBackdropFilter) \ + V(SceneBuilder, pushClipPath) \ + V(SceneBuilder, pushClipRRect) \ + V(SceneBuilder, pushClipRect) \ + V(SceneBuilder, pushColorFilter) \ + V(SceneBuilder, pushImageFilter) \ + V(SceneBuilder, pushOffset) \ + V(SceneBuilder, pushOpacity) \ + V(SceneBuilder, pushShaderMask) \ + V(SceneBuilder, pushTransformHandle) \ + V(Scene, dispose) \ + V(Scene, toImage) \ + V(Scene, toImageSync) \ + V(SemanticsUpdateBuilder, build) \ + V(SemanticsUpdateBuilder, updateCustomAction) \ + V(SemanticsUpdateBuilder, updateNode) \ + V(SemanticsUpdate, dispose) \ V(Vertices, dispose) #ifdef IMPELLER_ENABLE_3D diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 28eca84df7c63..6f33203fb8593 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -3756,9 +3756,46 @@ abstract class ImageFilter { return _ComposeImageFilter(innerFilter: inner, outerFilter: outer); } + /// Creates an image filter from a [FragmentShader]. + /// + /// The fragment shader provided here has additional requirements to be used + /// by the engine for filtering. The first uniform value must be a vec2, this + /// will be set by the engine to the size of the bound texture. There must + /// also be at least one sampler2D uniform, the first of which will be set by + /// the engine to contain the filter input. + /// + /// For example, the following is a valid fragment shader that can be used + /// with this API. Note that the uniform names are not required to have any + /// particular value. + /// + /// ```glsl + /// #include + /// + /// uniform vec2 u_size; + /// uniform float u_time; + /// + /// uniform sampler2D u_texture_input; + /// + /// out vec4 frag_color; + /// + /// void main() { + /// frag_color = texture(u_texture_input, FlutterFragCoord().xy / u_size) * u_time; + /// + /// } + /// + /// ``` + /// + /// This API is only supported when using the Impeller rendering engine. On + /// other backends a [UnsupportedError] will be thrown. This error can be + /// caught and used for feature detection. factory ImageFilter.shader(FragmentShader shader) { if (!_impellerEnabled) { - throw StateError('ImageFilter.shader only support with Impeller rendering engine.'); + throw UnsupportedError('ImageFilter.shader only support with Impeller rendering engine.'); + } + if (shader._floats.length < 2 || !shader._validateImageFilter()) { + throw StateError( + 'ImageFilter.shader requires that the first uniform is a ' + 'vec2 and at least one sampler uniform is present.'); } return _FragmentShaderImageFilter(shader); } @@ -4691,6 +4728,9 @@ base class FragmentShader extends Shader { @Native)>(symbol: 'ReusableFragmentShader::ValidateSamplers') external bool _validateSamplers(); + @Native)>(symbol: 'ReusableFragmentShader::ValidateImageFilter') + external bool _validateImageFilter(); + @Native)>(symbol: 'ReusableFragmentShader::Dispose') external void _dispose(); } diff --git a/lib/ui/painting/fragment_shader.cc b/lib/ui/painting/fragment_shader.cc index 977aa02180579..1300ab6f2d4b7 100644 --- a/lib/ui/painting/fragment_shader.cc +++ b/lib/ui/painting/fragment_shader.cc @@ -9,15 +9,8 @@ #include "flutter/display_list/dl_tile_mode.h" #include "flutter/display_list/effects/dl_color_source.h" -#include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/fragment_program.h" -#include "flutter/lib/ui/ui_dart_state.h" -#include "third_party/skia/include/core/SkString.h" #include "third_party/tonic/converter/dart_converter.h" -#include "third_party/tonic/dart_args.h" -#include "third_party/tonic/dart_binding_macros.h" -#include "third_party/tonic/dart_library_natives.h" -#include "third_party/tonic/typed_data/typed_list.h" namespace flutter { @@ -55,7 +48,7 @@ Dart_Handle ReusableFragmentShader::Create(Dart_Handle wrapper, } bool ReusableFragmentShader::ValidateSamplers() { - for (auto i = 0u; i < samplers_.size(); i += 1) { + for (auto i = 0u; i < samplers_.size(); i++) { if (samplers_[i] == nullptr) { return false; } @@ -101,7 +94,6 @@ std::shared_ptr ReusableFragmentShader::shader( // continually changed on the UI thread. So we take a copy of the uniforms // before handing it to the DisplayList for consumption on the render thread. auto uniform_data = std::make_shared>(); - FML_LOG(ERROR) << "Uniform data size: " << uniform_data_->size(); uniform_data->resize(uniform_data_->size()); memcpy(uniform_data->data(), uniform_data_->bytes(), uniform_data->size()); @@ -112,6 +104,24 @@ std::shared_ptr ReusableFragmentShader::shader( return source; } +// Image filters require at least one uniform sampler input to bind +// the input texture. +bool ReusableFragmentShader::ValidateImageFilter() { + if (samplers_.size() < 1) { + return false; + } + // The first sampler does not need to be set. + for (auto i = 1u; i < samplers_.size(); i++) { + if (samplers_[i] == nullptr) { + return false; + } + // The samplers should have been checked as they were added, this + // is a double-sanity-check. + FML_DCHECK(samplers_[i]->isUIThreadSafe()); + } + return true; +} + void ReusableFragmentShader::Dispose() { uniform_data_.reset(); program_ = nullptr; diff --git a/lib/ui/painting/fragment_shader.h b/lib/ui/painting/fragment_shader.h index e6756b09edd30..a8a89ea816432 100644 --- a/lib/ui/painting/fragment_shader.h +++ b/lib/ui/painting/fragment_shader.h @@ -38,6 +38,8 @@ class ReusableFragmentShader : public Shader { bool ValidateSamplers(); + bool ValidateImageFilter(); + void Dispose(); // |Shader| From 1f206da811e0bf0023300bc55e5540ccd79eac7c Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 20 Jun 2024 09:58:08 -0700 Subject: [PATCH 05/38] rename --- display_list/dl_builder.cc | 2 +- display_list/effects/dl_image_filter.h | 23 +++++++++++------------ display_list/skia/dl_sk_conversions.cc | 2 +- impeller/display_list/dl_dispatcher.cc | 4 ++-- lib/ui/painting/image_filter.cc | 2 +- testing/display_list_testing.cc | 8 ++++---- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc index 381f5e2f5a493..143d891256819 100644 --- a/display_list/dl_builder.cc +++ b/display_list/dl_builder.cc @@ -297,7 +297,7 @@ void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) { case DlImageFilterType::kCompose: case DlImageFilterType::kLocalMatrix: case DlImageFilterType::kColorFilter: - case DlImageFilterType::kFragmentProgram: { + case DlImageFilterType::kRuntimeEffect: { Push(0, filter); break; } diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h index 9fd7e9cc809d2..288288547113f 100644 --- a/display_list/effects/dl_image_filter.h +++ b/display_list/effects/dl_image_filter.h @@ -35,7 +35,7 @@ enum class DlImageFilterType { kCompose, kColorFilter, kLocalMatrix, - kFragmentProgram, + kRuntimeEffect, }; class DlBlurImageFilter; @@ -45,7 +45,7 @@ class DlMatrixImageFilter; class DlLocalMatrixImageFilter; class DlComposeImageFilter; class DlColorFilterImageFilter; -class DlFragmentProgramImageFilter; +class DlRuntimeEffectImageFilter; class DlImageFilter : public DlAttribute { public: @@ -88,9 +88,9 @@ class DlImageFilter : public DlAttribute { return nullptr; } - // Return a DlComposeImageFilter pointer to this object iff it is a - // DlFragmentProgramImageFilter type of ImageFilter, otherwise return nullptr. - virtual const DlFragmentProgramImageFilter* asFragmentProgramFilter() const { + // Return a DlRuntimeEffectImageFilter pointer to this object iff it is a + // DlRuntimeEffectImageFilter type of ImageFilter, otherwise return nullptr. + virtual const DlRuntimeEffectImageFilter* asRuntimeEffectFilter() const { return nullptr; } @@ -750,27 +750,26 @@ class DlLocalMatrixImageFilter final : public DlImageFilter { std::shared_ptr image_filter_; }; -class DlFragmentProgramImageFilter final : public DlImageFilter { +class DlRuntimeEffectImageFilter final : public DlImageFilter { public: - explicit DlFragmentProgramImageFilter( + explicit DlRuntimeEffectImageFilter( std::shared_ptr runtime_effect) : runtime_effect_(std::move(runtime_effect)) { FML_DCHECK(!!runtime_effect_->asRuntimeEffect()); } std::shared_ptr shared() const override { - return std::make_shared( - this->runtime_effect_); + return std::make_shared(this->runtime_effect_); } static std::shared_ptr Make( std::shared_ptr runtime_effect) { - return std::make_shared( + return std::make_shared( std::move(runtime_effect)); } DlImageFilterType type() const override { - return DlImageFilterType::kFragmentProgram; + return DlImageFilterType::kRuntimeEffect; } size_t size() const override { return sizeof(*this); } @@ -800,7 +799,7 @@ class DlFragmentProgramImageFilter final : public DlImageFilter { return &input_bounds; } - const DlFragmentProgramImageFilter* asFragmentProgramFilter() const override { + const DlRuntimeEffectImageFilter* asRuntimeEffectFilter() const override { return this; } diff --git a/display_list/skia/dl_sk_conversions.cc b/display_list/skia/dl_sk_conversions.cc index c85cc2fc78809..5531fdf977af6 100644 --- a/display_list/skia/dl_sk_conversions.cc +++ b/display_list/skia/dl_sk_conversions.cc @@ -229,7 +229,7 @@ sk_sp ToSk(const DlImageFilter* filter) { } return skia_filter->makeWithLocalMatrix(lm_filter->matrix()); } - case DlImageFilterType::kFragmentProgram: + case DlImageFilterType::kRuntimeEffect: // UNSUPPORTED. return nullptr; break; diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index b0540cd2122eb..d7fac60f84d89 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -600,8 +600,8 @@ static std::shared_ptr ToImageFilter( auto matrix = ToMatrix(local_matrix_filter->matrix()); return ImageFilter::MakeLocalMatrix(matrix, *image_filter); } - case flutter::DlImageFilterType::kFragmentProgram: - auto fragment_program_filter = filter->asFragmentProgramFilter(); + case flutter::DlImageFilterType::kRuntimeEffect: + auto fragment_program_filter = filter->asRuntimeEffectFilter(); FML_DCHECK(fragment_program_filter); const flutter::DlRuntimeEffectColorSource* runtime_effect_color_source = fragment_program_filter->GetRuntimeEffect()->asRuntimeEffect(); diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index e9248a493465a..fb117a0839bc1 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -90,7 +90,7 @@ void ImageFilter::initComposeFilter(ImageFilter* outer, ImageFilter* inner) { void ImageFilter::initShader(ReusableFragmentShader* shader) { FML_DCHECK(shader); - filter_ = DlFragmentProgramImageFilter::Make( + filter_ = DlRuntimeEffectImageFilter::Make( shader->shader(DlImageSampling::kNearestNeighbor)); } diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index 9d898f4db1457..29e9a5e2a5478 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -632,10 +632,10 @@ void DisplayListStreamDispatcher::out(const DlImageFilter& filter) { startl() << ")"; break; } - case flutter::DlImageFilterType::kFragmentProgram: { - const DlFragmentProgramImageFilter* fragment = filter.asFragmentProgramFilter(); - FML_DCHECK(fragment); - os_ << "DlFragmentProgramImageFilter()"; + case flutter::DlImageFilterType::kRuntimeEffect: { + const DlRuntimeEffectImageFilter* runtime_effect = filter.asRuntimeEffectFilter(); + FML_DCHECK(runtime_effect); + os_ << "DlRuntimeEffectImageFilter()"; break; } } From 0ec37a8c70b45a75864884ae079c0f1f92df502a Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 20 Jun 2024 10:37:58 -0700 Subject: [PATCH 06/38] ++ --- ci/licenses_golden/licenses_flutter | 4 ++++ lib/ui/painting.dart | 2 +- lib/web_ui/lib/painting.dart | 4 ++++ testing/display_list_testing.cc | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 70cbd7942ea7f..a6bf78a7f3bce 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -42011,6 +42011,8 @@ ORIGIN: ../../../flutter/impeller/entity/contents/filters/matrix_filter_contents ORIGIN: ../../../flutter/impeller/entity/contents/filters/matrix_filter_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/filters/morphology_filter_contents.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/filters/morphology_filter_contents.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/contents/filters/runtime_effect_filter_contents.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/contents/filters/runtime_effect_filter_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc + ../../../flutter/LICENSE @@ -44875,6 +44877,8 @@ FILE: ../../../flutter/impeller/entity/contents/filters/matrix_filter_contents.c FILE: ../../../flutter/impeller/entity/contents/filters/matrix_filter_contents.h FILE: ../../../flutter/impeller/entity/contents/filters/morphology_filter_contents.cc FILE: ../../../flutter/impeller/entity/contents/filters/morphology_filter_contents.h +FILE: ../../../flutter/impeller/entity/contents/filters/runtime_effect_filter_contents.cc +FILE: ../../../flutter/impeller/entity/contents/filters/runtime_effect_filter_contents.h FILE: ../../../flutter/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc FILE: ../../../flutter/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h FILE: ../../../flutter/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 6f33203fb8593..5d8904b9a8dcf 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -3790,7 +3790,7 @@ abstract class ImageFilter { /// caught and used for feature detection. factory ImageFilter.shader(FragmentShader shader) { if (!_impellerEnabled) { - throw UnsupportedError('ImageFilter.shader only support with Impeller rendering engine.'); + throw UnsupportedError('ImageFilter.shader only supported with Impeller rendering engine.'); } if (shader._floats.length < 2 || !shader._validateImageFilter()) { throw StateError( diff --git a/lib/web_ui/lib/painting.dart b/lib/web_ui/lib/painting.dart index 6369511173041..5b3e23cb313aa 100644 --- a/lib/web_ui/lib/painting.dart +++ b/lib/web_ui/lib/painting.dart @@ -448,6 +448,10 @@ class ImageFilter { factory ImageFilter.compose({required ImageFilter outer, required ImageFilter inner}) => engine.renderer.composeImageFilters(outer: outer, inner: inner); + + factory ImageFilter.shader(FragmentShader shader) { + throw UnsupportedError('ImageFilter.shader only supported with Impeller rendering engine.'); + } } enum ColorSpace { diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index 29e9a5e2a5478..6978a6f5d5655 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -633,7 +633,7 @@ void DisplayListStreamDispatcher::out(const DlImageFilter& filter) { break; } case flutter::DlImageFilterType::kRuntimeEffect: { - const DlRuntimeEffectImageFilter* runtime_effect = filter.asRuntimeEffectFilter(); + [[maybe_unused]] const DlRuntimeEffectImageFilter* runtime_effect = filter.asRuntimeEffectFilter(); FML_DCHECK(runtime_effect); os_ << "DlRuntimeEffectImageFilter()"; break; From c3d1e0d2f39af964d2cc0c3eeb3b98613a0efd0b Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 20 Jun 2024 11:12:46 -0700 Subject: [PATCH 07/38] add ignore --- lib/web_ui/lib/painting.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web_ui/lib/painting.dart b/lib/web_ui/lib/painting.dart index 5b3e23cb313aa..bb5d43bdae3b6 100644 --- a/lib/web_ui/lib/painting.dart +++ b/lib/web_ui/lib/painting.dart @@ -449,6 +449,7 @@ class ImageFilter { factory ImageFilter.compose({required ImageFilter outer, required ImageFilter inner}) => engine.renderer.composeImageFilters(outer: outer, inner: inner); + // ignore: avoid_unused_constructor_parameters factory ImageFilter.shader(FragmentShader shader) { throw UnsupportedError('ImageFilter.shader only supported with Impeller rendering engine.'); } From c4ce95dd260d3ea70af9c8bbe47828733251ef85 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 24 Jun 2024 09:21:18 -0700 Subject: [PATCH 08/38] add unit testing. --- .../fixtures/shaders/general_shaders/BUILD.gn | 1 + .../general_shaders/filter_shader.frag | 15 +++++++ testing/dart/fragment_shader_test.dart | 43 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 lib/ui/fixtures/shaders/general_shaders/filter_shader.frag diff --git a/lib/ui/fixtures/shaders/general_shaders/BUILD.gn b/lib/ui/fixtures/shaders/general_shaders/BUILD.gn index 9e9eb0be00812..47b7a92dd4276 100644 --- a/lib/ui/fixtures/shaders/general_shaders/BUILD.gn +++ b/lib/ui/fixtures/shaders/general_shaders/BUILD.gn @@ -17,6 +17,7 @@ if (enable_unittests) { "uniforms.frag", "uniforms_sorted.frag", "uniform_arrays.frag", + "filter_shader.frag", ] group("general_shaders") { diff --git a/lib/ui/fixtures/shaders/general_shaders/filter_shader.frag b/lib/ui/fixtures/shaders/general_shaders/filter_shader.frag new file mode 100644 index 0000000000000..fdfceb76ef473 --- /dev/null +++ b/lib/ui/fixtures/shaders/general_shaders/filter_shader.frag @@ -0,0 +1,15 @@ +// 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 + +uniform vec2 u_size; +uniform sampler2D u_texture; + +out vec4 frag_color; + +void main() { + // swap color channels. + frag_color = texture(u_texture, FlutterFragCoord().xy / u_size).bgra; +} diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index 4d339029ce301..0989d0535fd32 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -392,6 +392,49 @@ void main() async { shader.dispose(); }); + test('ImageFilter.shader errors if shader does not have correct uniform layout', () async { + if (!impellerEnabled) { + print('Skipped for Skia'); + return; + } + final FragmentProgram program = await FragmentProgram.fromAsset( + 'no_uniforms.frag.iplr', + ); + final FragmentShader shader = program.fragmentShader(); + + Object? error; + try { + ImageFilter.shader(shader); + } catch (err) { + error = err; + } + expect(error is StateError, true); + }); + + test('ImageFilter.shader can be applied to canvas operations', () async { + if (!impellerEnabled) { + print('Skipped for Skia'); + return; + } + final FragmentProgram program = await FragmentProgram.fromAsset( + 'filter_shader.frag.iplr', + ); + final FragmentShader shader = program.fragmentShader(); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawPaint( + Paint() + ..color = const Color(0xFFFF0000) + ..imageFilter = ImageFilter.shader(shader) + ); + final Image image = await recorder.endRecording().toImage(1, 1); + final ByteData data = (await image.toByteData())!; + final Color color = Color(data.buffer.asUint32List()[0]); + print(color); + + expect(color, const Color(0xFF00FF00)); + }); + if (impellerEnabled) { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; From b07ef04b0ec67e8f5d4f60872427e106ce8c12b2 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 24 Jun 2024 09:56:23 -0700 Subject: [PATCH 09/38] fix include. --- lib/ui/fixtures/shaders/general_shaders/filter_shader.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/fixtures/shaders/general_shaders/filter_shader.frag b/lib/ui/fixtures/shaders/general_shaders/filter_shader.frag index fdfceb76ef473..a7181e09060c9 100644 --- a/lib/ui/fixtures/shaders/general_shaders/filter_shader.frag +++ b/lib/ui/fixtures/shaders/general_shaders/filter_shader.frag @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include +#include uniform vec2 u_size; uniform sampler2D u_texture; From 216b604e6675085e8926e3e1e2fcab05c93e0192 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 24 Jun 2024 11:28:27 -0700 Subject: [PATCH 10/38] add aiks test. --- impeller/aiks/aiks_unittests.cc | 25 +++++++++++++++++++ impeller/fixtures/BUILD.gn | 1 + .../runtime_stage_filter_example.frag | 14 +++++++++++ testing/dart/fragment_shader_test.dart | 1 - 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 impeller/fixtures/runtime_stage_filter_example.frag diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 1525d9f9087d7..2860c0f56ad90 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -27,6 +27,7 @@ #include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" +#include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/size.h" #include "impeller/playground/widgets.h" @@ -2173,6 +2174,30 @@ TEST_P(AiksTest, CanRenderClippedRuntimeEffects) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { + auto runtime_stages = + OpenAssetAsRuntimeStage("runtime_stage_example_filter.frag.iplr"); + + auto runtime_stage = + runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())]; + ASSERT_TRUE(runtime_stage); + ASSERT_TRUE(runtime_stage->IsDirty()); + + std::vector texture_inputs; + auto uniform_data = std::make_shared>(); + uniform_data->resize(sizeof(Vector2)); + + Paint paint; + paint.color = Color::Aqua(); + paint.image_filter = ImageFilter::MakeRuntimeEffect( + runtime_stage, uniform_data, texture_inputs); + + Canvas canvas; + canvas.DrawRect(Rect::MakeXYWH(0, 0, 400, 400), paint); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_P(AiksTest, DrawPaintTransformsBounds) { auto runtime_stages = OpenAssetAsRuntimeStage("gradient.frag.iplr"); auto runtime_stage = diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 2253fad68b414..64242e0e331a9 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -69,6 +69,7 @@ impellerc("runtime_stages") { shaders = [ "ink_sparkle.frag", "runtime_stage_example.frag", + "runtime_stage_filter_example.frag", "gradient.frag", "uniforms_and_sampler_1.frag", "uniforms_and_sampler_2.frag", diff --git a/impeller/fixtures/runtime_stage_filter_example.frag b/impeller/fixtures/runtime_stage_filter_example.frag new file mode 100644 index 0000000000000..c7bcea0ba45d1 --- /dev/null +++ b/impeller/fixtures/runtime_stage_filter_example.frag @@ -0,0 +1,14 @@ +// 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 + +uniform vec2 u_size; +uniform sampler2D u_texture; + +out vec4 frag_color; + +void main() { + frag_color = texture(u_texture, FlutterFragCoord().xy / u_size); +} diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index 0989d0535fd32..89f6167274f6a 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -430,7 +430,6 @@ void main() async { final Image image = await recorder.endRecording().toImage(1, 1); final ByteData data = (await image.toByteData())!; final Color color = Color(data.buffer.asUint32List()[0]); - print(color); expect(color, const Color(0xFF00FF00)); }); From e85199979ba185a3ea583f79e4f7d8f3c98877c7 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 24 Jun 2024 12:38:11 -0700 Subject: [PATCH 11/38] ++ --- impeller/aiks/aiks_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 2860c0f56ad90..a55486905c089 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -2176,7 +2176,7 @@ TEST_P(AiksTest, CanRenderClippedRuntimeEffects) { TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { auto runtime_stages = - OpenAssetAsRuntimeStage("runtime_stage_example_filter.frag.iplr"); + OpenAssetAsRuntimeStage("runtime_stage_filter_example.frag.iplr"); auto runtime_stage = runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())]; From 217253b317a898de0ebf02fb7a93484ecb591f4b Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 24 Jun 2024 13:15:57 -0700 Subject: [PATCH 12/38] ++ --- impeller/aiks/aiks_unittests.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index a55486905c089..ea950a0ad38f4 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -20,6 +20,7 @@ #include "impeller/aiks/image_filter.h" #include "impeller/aiks/testing/context_spy.h" #include "impeller/core/device_buffer.h" +#include "impeller/core/sampler_descriptor.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/geometry/color.h" #include "impeller/geometry/constants.h" @@ -2183,7 +2184,13 @@ TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { ASSERT_TRUE(runtime_stage); ASSERT_TRUE(runtime_stage->IsDirty()); - std::vector texture_inputs; + std::vector texture_inputs = { + RuntimeEffectContents::TextureInput{ + .sampler_descriptor = SamplerDescriptor{}, + .texture = nullptr, + } + }; + auto uniform_data = std::make_shared>(); uniform_data->resize(sizeof(Vector2)); From 6ead4352fbb8ca12bd60af1673dab58537e62828 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 24 Jun 2024 13:31:28 -0700 Subject: [PATCH 13/38] ++ --- impeller/aiks/aiks_unittests.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index ea950a0ad38f4..c618f89f3fdba 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -2185,11 +2185,10 @@ TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { ASSERT_TRUE(runtime_stage->IsDirty()); std::vector texture_inputs = { - RuntimeEffectContents::TextureInput{ - .sampler_descriptor = SamplerDescriptor{}, - .texture = nullptr, - } - }; + RuntimeEffectContents::TextureInput{ + .sampler_descriptor = SamplerDescriptor{}, + .texture = nullptr, + }}; auto uniform_data = std::make_shared>(); uniform_data->resize(sizeof(Vector2)); From d848d669e0cde0a947188511db54c9ce3843ee66 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 24 Jun 2024 15:27:31 -0700 Subject: [PATCH 14/38] 8 uint 8s? --- .../entity/contents/filters/runtime_effect_filter_contents.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc index 3bb8bffa80fba..06e0dc5e5e803 100644 --- a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc @@ -34,7 +34,7 @@ std::optional RuntimeEffectFilterContents::RenderFilter( // which is treated as the input and a vec2 size uniform to compute the // offsets. These are validated at the dart:ui layer, but to avoid crashes we // check here too. - if (texture_inputs_.size() < 1 || uniforms_->size() < 16) { + if (texture_inputs_.size() < 1 || uniforms_->size() < 8) { VALIDATION_LOG << "Invalid fragment shader in RuntimeEffectFilterContents. " << "Shader must have at least one sampler and a vec2 size uniform."; From 59cecc3b31bffdaed8c54857378dce84e344f022 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 24 Jun 2024 19:10:20 -0700 Subject: [PATCH 15/38] not sure why this works. --- impeller/aiks/aiks_unittests.cc | 4 ++++ .../entity/contents/filters/runtime_effect_filter_contents.cc | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index c618f89f3fdba..7c46b2a28a0e6 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -31,6 +31,7 @@ #include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/size.h" +#include "impeller/playground/playground.h" #include "impeller/playground/widgets.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/snapshot.h" @@ -2176,6 +2177,9 @@ TEST_P(AiksTest, CanRenderClippedRuntimeEffects) { } TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { + if (GetParam() == PlaygroundBackend::kOpenGLES) { + GTEST_SKIP() << "Not currently supported on OpenGLES backend."; + } auto runtime_stages = OpenAssetAsRuntimeStage("runtime_stage_filter_example.frag.iplr"); diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc index 06e0dc5e5e803..96b00656c5444 100644 --- a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc @@ -26,7 +26,7 @@ std::optional RuntimeEffectFilterContents::RenderFilter( } auto input_snapshot = - inputs[0]->GetSnapshot("RuntimeEffectContents", renderer, entity); + inputs[0]->GetSnapshot("RuntimeEffectContents", renderer, {}); if (!input_snapshot.has_value()) { return std::nullopt; } @@ -71,6 +71,7 @@ std::optional RuntimeEffectFilterContents::RenderFilter( Entity sub_entity; sub_entity.SetContents(std::move(contents)); sub_entity.SetBlendMode(entity.GetBlendMode()); + sub_entity.SetTransform(entity.GetTransform()); return sub_entity; } From cda52b03bcf6fbb783efe25b1c9e6398a2164e6e Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 5 Jul 2024 12:17:51 -0700 Subject: [PATCH 16/38] fixups for entity transform. --- .../filters/runtime_effect_filter_contents.cc | 47 +++++++++++++++---- .../filters/runtime_effect_filter_contents.h | 19 +++----- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc index 96b00656c5444..df0ca708f6158 100644 --- a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc @@ -3,16 +3,33 @@ // found in the LICENSE file. #include "impeller/entity/contents/filters/runtime_effect_filter_contents.h" + #include +#include + #include "impeller/base/validation.h" #include "impeller/entity/contents/anonymous_contents.h" #include "impeller/entity/contents/runtime_effect_contents.h" #include "impeller/entity/geometry/geometry.h" -#include "impeller/geometry/point.h" #include "impeller/geometry/size.h" namespace impeller { +void RuntimeEffectFilterContents::SetRuntimeStage( + std::shared_ptr runtime_stage) { + runtime_stage_ = std::move(runtime_stage); +} + +void RuntimeEffectFilterContents::SetUniforms( + std::shared_ptr> uniforms) { + uniforms_ = std::move(uniforms); +} + +void RuntimeEffectFilterContents::SetTextureInputs( + std::vector texture_inputs) { + texture_inputs_ = std::move(texture_inputs); +} + // |FilterContents| std::optional RuntimeEffectFilterContents::RenderFilter( const FilterInput::Vector& inputs, @@ -26,10 +43,15 @@ std::optional RuntimeEffectFilterContents::RenderFilter( } auto input_snapshot = - inputs[0]->GetSnapshot("RuntimeEffectContents", renderer, {}); + inputs[0]->GetSnapshot("RuntimeEffectContents", renderer, entity); if (!input_snapshot.has_value()) { return std::nullopt; } + std::optional maybe_input_coverage = input_snapshot->GetCoverage(); + if (!maybe_input_coverage.has_value()) { + return std::nullopt; + } + Rect input_coverage = maybe_input_coverage.value(); // The shader is required to have at least one sampler, the first of // which is treated as the input and a vec2 size uniform to compute the // offsets. These are validated at the dart:ui layer, but to avoid crashes we @@ -40,24 +62,29 @@ std::optional RuntimeEffectFilterContents::RenderFilter( << "Shader must have at least one sampler and a vec2 size uniform."; return std::nullopt; } - texture_inputs_[0].texture = input_snapshot->texture; + + // Update uniform values. + std::vector texture_input_copy = + texture_inputs_; + texture_input_copy[0].texture = input_snapshot->texture; + Size size = Size(input_snapshot->texture->GetSize()); memcpy(uniforms_->data(), &size, sizeof(Size)); //---------------------------------------------------------------------------- /// Create AnonymousContents for rendering. /// - RenderProc render_proc = [input_snapshot, runtime_stage = runtime_stage_, - uniforms = uniforms_, - texture_inputs = texture_inputs_]( - const ContentContext& renderer, - const Entity& entity, RenderPass& pass) -> bool { + RenderProc render_proc = + [input_snapshot, runtime_stage = runtime_stage_, uniforms = uniforms_, + texture_inputs = texture_input_copy, + input_coverage](const ContentContext& renderer, const Entity& entity, + RenderPass& pass) -> bool { RuntimeEffectContents contents; contents.SetRuntimeStage(runtime_stage); contents.SetUniformData(uniforms); contents.SetTextureInputs(texture_inputs); contents.SetGeometry( - Geometry::MakeRect(input_snapshot->GetCoverage().value())); + Geometry::MakeRect(Rect::MakeSize(input_coverage.GetSize()))); return contents.Render(renderer, entity, pass); }; @@ -71,7 +98,7 @@ std::optional RuntimeEffectFilterContents::RenderFilter( Entity sub_entity; sub_entity.SetContents(std::move(contents)); sub_entity.SetBlendMode(entity.GetBlendMode()); - sub_entity.SetTransform(entity.GetTransform()); + sub_entity.SetTransform(input_snapshot->transform); return sub_entity; } diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.h b/impeller/entity/contents/filters/runtime_effect_filter_contents.h index c6a737e0e9c55..3a68ecd67ab15 100644 --- a/impeller/entity/contents/filters/runtime_effect_filter_contents.h +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.h @@ -9,29 +9,24 @@ namespace impeller { +/// A filter that applies a runtime effect shader class RuntimeEffectFilterContents final : public FilterContents { public: RuntimeEffectFilterContents() {} - ~RuntimeEffectFilterContents() {} + ~RuntimeEffectFilterContents() = default; - void SetRuntimeStage(std::shared_ptr runtime_stage) { - runtime_stage_ = std::move(runtime_stage); - } + void SetRuntimeStage(std::shared_ptr runtime_stage); - void SetUniforms(std::shared_ptr> uniforms) { - uniforms_ = std::move(uniforms); - } + void SetUniforms(std::shared_ptr> uniforms); void SetTextureInputs( - std::vector texture_inputs) { - texture_inputs_ = std::move(texture_inputs); - } + std::vector texture_inputs); private: std::shared_ptr runtime_stage_; - mutable std::shared_ptr> uniforms_; - mutable std::vector texture_inputs_; + std::shared_ptr> uniforms_; + std::vector texture_inputs_; // |FilterContents| std::optional RenderFilter( From 77e290bf60ea68e885efd055f911062e3cf26884 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 5 Jul 2024 12:43:58 -0700 Subject: [PATCH 17/38] ++ --- testing/impeller_golden_tests_output.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing/impeller_golden_tests_output.txt b/testing/impeller_golden_tests_output.txt index f1b609c0f30eb..7376fa9652dae 100644 --- a/testing/impeller_golden_tests_output.txt +++ b/testing/impeller_golden_tests_output.txt @@ -394,6 +394,8 @@ impeller_Play_AiksTest_CanRenderRadialGradient_Vulkan.png impeller_Play_AiksTest_CanRenderRoundedRectWithNonUniformRadii_Metal.png impeller_Play_AiksTest_CanRenderRoundedRectWithNonUniformRadii_OpenGLES.png impeller_Play_AiksTest_CanRenderRoundedRectWithNonUniformRadii_Vulkan.png +impeller_Play_AiksTest_CanRenderRuntimeEffectFilter_Metal.png +impeller_Play_AiksTest_CanRenderRuntimeEffectFilter_Vulkan.png impeller_Play_AiksTest_CanRenderSimpleClips_Metal.png impeller_Play_AiksTest_CanRenderSimpleClips_OpenGLES.png impeller_Play_AiksTest_CanRenderSimpleClips_Vulkan.png From 7772b8cfc01043aad4612c707166e702a3a2edb0 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 13 Aug 2024 10:39:45 -0700 Subject: [PATCH 18/38] ++ --- lib/ui/painting.dart | 7 +++++-- lib/web_ui/lib/painting.dart | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 33f6ed02d667d..3cbb4258fa92a 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -3788,8 +3788,8 @@ abstract class ImageFilter { /// ``` /// /// This API is only supported when using the Impeller rendering engine. On - /// other backends a [UnsupportedError] will be thrown. This error can be - /// caught and used for feature detection. + /// other backends a [UnsupportedError] will be thrown. To check at runtime + /// whether this API is suppored use [isShaderFilterSupported]. factory ImageFilter.shader(FragmentShader shader) { if (!_impellerEnabled) { throw UnsupportedError('ImageFilter.shader only supported with Impeller rendering engine.'); @@ -3802,6 +3802,9 @@ abstract class ImageFilter { return _FragmentShaderImageFilter(shader); } + /// Whether [ImageFilter.shader] is supported on the current backend. + static bool get isShaderFilterSupported => _impellerEnabled; + // Converts this to a native DlImageFilter. See the comments of this method in // subclasses for the exact type of DlImageFilter this method converts to. _ImageFilter _toNativeImageFilter(); diff --git a/lib/web_ui/lib/painting.dart b/lib/web_ui/lib/painting.dart index bb5d43bdae3b6..74a03dcd9135d 100644 --- a/lib/web_ui/lib/painting.dart +++ b/lib/web_ui/lib/painting.dart @@ -453,6 +453,8 @@ class ImageFilter { factory ImageFilter.shader(FragmentShader shader) { throw UnsupportedError('ImageFilter.shader only supported with Impeller rendering engine.'); } + + static bool get isShaderFilterSupported => false; } enum ColorSpace { From beac90a34706d3066c9c9c3eae095e8243a0f958 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 13 Aug 2024 11:15:00 -0700 Subject: [PATCH 19/38] ++ --- .../aiks_dl_runtime_effect_unittests.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc index 1d332290e6131..6747858ca0882 100644 --- a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc +++ b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc @@ -6,6 +6,7 @@ #include "display_list/effects/dl_color_source.h" #include "display_list/effects/dl_image_filter.h" +#include "display_list/effects/dl_runtime_effect.h" #include "display_list/image/dl_image.h" #include "flutter/impeller/aiks/aiks_unittests.h" @@ -97,19 +98,18 @@ TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { ASSERT_TRUE(runtime_stage); ASSERT_TRUE(runtime_stage->IsDirty()); - std::vector texture_inputs = { - RuntimeEffectContents::TextureInput{ - .sampler_descriptor = SamplerDescriptor{}, - .texture = nullptr, - }}; - + std::vector> sampler_inputs = { + nullptr, + }; auto uniform_data = std::make_shared>(); uniform_data->resize(sizeof(Vector2)); DlPaint paint; paint.setColor(DlColor::kAqua()); paint.setImageFilter(std::make_shared( - runtime_stage, uniform_data, texture_inputs)); + DlColorSource::MakeRuntimeEffect( + DlRuntimeEffect::MakeImpeller(runtime_stage), sampler_inputs, + uniform_data))); DisplayListBuilder builder; builder.DrawRect(SkRect::MakeXYWH(0, 0, 400, 400), paint); From eab80e0b77ec03efd191a23624712ba8c1faa81a Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 13 Aug 2024 11:16:27 -0700 Subject: [PATCH 20/38] ++ --- impeller/display_list/aiks_dl_runtime_effect_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc index 6747858ca0882..2fd965f952d56 100644 --- a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc +++ b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc @@ -99,7 +99,7 @@ TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { ASSERT_TRUE(runtime_stage->IsDirty()); std::vector> sampler_inputs = { - nullptr, + nullptr, }; auto uniform_data = std::make_shared>(); uniform_data->resize(sizeof(Vector2)); From 2f9dc4796473242fa90d6fd8879cc8ea6c6de896 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 13 Aug 2024 12:21:17 -0700 Subject: [PATCH 21/38] ++ --- impeller/aiks/image_filter.cc | 13 +++++++ impeller/aiks/image_filter.h | 12 +++--- .../aiks_dl_runtime_effect_unittests.cc | 3 -- impeller/renderer/command.cc | 38 ++++++++++++------- impeller/renderer/command.h | 8 ++-- 5 files changed, 48 insertions(+), 26 deletions(-) diff --git a/impeller/aiks/image_filter.cc b/impeller/aiks/image_filter.cc index dd91c38fa8f4e..46eea34ab102b 100644 --- a/impeller/aiks/image_filter.cc +++ b/impeller/aiks/image_filter.cc @@ -155,6 +155,8 @@ std::shared_ptr MatrixImageFilter::Clone() const { return std::make_shared(*this); } +void MatrixImageFilter::Visit(ImageFilterVisitor& visitor) { visitor.Visit(*this); } + /******************************************************************************* ******* ComposeImageFilter ******************************************************************************/ @@ -174,6 +176,9 @@ std::shared_ptr ComposeImageFilter::Clone() const { return std::make_shared(*this); } +void ComposeImageFilter::Visit(ImageFilterVisitor& visitor) { visitor.Visit(*this); } + + /******************************************************************************* ******* ColorImageFilter ******************************************************************************/ @@ -193,6 +198,8 @@ std::shared_ptr ColorImageFilter::Clone() const { return std::make_shared(*this); } +void ColorImageFilter::Visit(ImageFilterVisitor& visitor) { visitor.Visit(*this); } + /******************************************************************************* ******* LocalMatrixImageFilter ******************************************************************************/ @@ -214,6 +221,8 @@ std::shared_ptr LocalMatrixImageFilter::Clone() const { return std::make_shared(*this); } +void LocalMatrixImageFilter::Visit(ImageFilterVisitor& visitor) { visitor.Visit(*this); } + /******************************************************************************* ******* RuntimeEffectImageFilter ******************************************************************************/ @@ -226,6 +235,8 @@ RuntimeEffectImageFilter::RuntimeEffectImageFilter( uniforms_(std::move(uniforms)), texture_inputs_(std::move(texture_inputs)) {} +RuntimeEffectImageFilter::~RuntimeEffectImageFilter() {} + // |ImageFilter| std::shared_ptr RuntimeEffectImageFilter::WrapInput( const FilterInput::Ref& input) const { @@ -238,4 +249,6 @@ std::shared_ptr RuntimeEffectImageFilter::Clone() const { return std::make_shared(*this); } +void RuntimeEffectImageFilter::Visit(ImageFilterVisitor& visitor) { visitor.Visit(*this); } + } // namespace impeller diff --git a/impeller/aiks/image_filter.h b/impeller/aiks/image_filter.h index 2733a43cb6356..49aaa24fe8738 100644 --- a/impeller/aiks/image_filter.h +++ b/impeller/aiks/image_filter.h @@ -200,7 +200,7 @@ class MatrixImageFilter : public ImageFilter { std::shared_ptr Clone() const override; // |ImageFilter| - void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + void Visit(ImageFilterVisitor& visitor) override; const Matrix& GetMatrix() const { return matrix_; } @@ -227,7 +227,7 @@ class ComposeImageFilter : public ImageFilter { std::shared_ptr Clone() const override; // |ImageFilter| - void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + void Visit(ImageFilterVisitor& visitor) override; private: std::shared_ptr inner_; @@ -252,7 +252,7 @@ class ColorImageFilter : public ImageFilter { std::shared_ptr Clone() const override; // |ImageFilter| - void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + void Visit(ImageFilterVisitor& visitor) override; private: std::shared_ptr color_filter_; @@ -277,7 +277,7 @@ class LocalMatrixImageFilter : public ImageFilter { std::shared_ptr Clone() const override; // |ImageFilter| - void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + void Visit(ImageFilterVisitor& visitor) override; private: Matrix matrix_; @@ -295,7 +295,7 @@ class RuntimeEffectImageFilter : public ImageFilter { std::shared_ptr> uniforms, std::vector texture_inputs); - ~RuntimeEffectImageFilter() = default; + ~RuntimeEffectImageFilter(); // |ImageFilter| std::shared_ptr WrapInput( @@ -305,7 +305,7 @@ class RuntimeEffectImageFilter : public ImageFilter { std::shared_ptr Clone() const override; // |ImageFilter| - void Visit(ImageFilterVisitor& visitor) override { visitor.Visit(*this); } + void Visit(ImageFilterVisitor& visitor) override; private: std::shared_ptr runtime_stage_; diff --git a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc index 2fd965f952d56..bed95831348ca 100644 --- a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc +++ b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc @@ -87,9 +87,6 @@ TEST_P(AiksTest, DrawPaintTransformsBounds) { } TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { - if (GetParam() == PlaygroundBackend::kOpenGLES) { - GTEST_SKIP() << "Not currently supported on OpenGLES backend."; - } auto runtime_stages = OpenAssetAsRuntimeStage("runtime_stage_filter_example.frag.iplr"); diff --git a/impeller/renderer/command.cc b/impeller/renderer/command.cc index 773ce08c40e78..05b38c84c7da5 100644 --- a/impeller/renderer/command.cc +++ b/impeller/renderer/command.cc @@ -27,7 +27,27 @@ bool Command::BindResource(ShaderStage stage, const ShaderUniformSlot& slot, const ShaderMetadata& metadata, BufferView view) { - return DoBindResource(stage, slot, &metadata, std::move(view)); + FML_DCHECK(slot.ext_res_0 != VertexDescriptor::kReservedVertexBufferIndex); + if (!view) { + return false; + } + + switch (stage) { + case ShaderStage::kVertex: + vertex_bindings.buffers.emplace_back(BufferAndUniformSlot{ + .slot = slot, .view = BufferResource(metadata, std::move(view))}); + return true; + case ShaderStage::kFragment: + fragment_bindings.buffers.emplace_back(BufferAndUniformSlot{ + .slot = slot, .view = BufferResource(metadata, std::move(view))}); + return true; + case ShaderStage::kCompute: + VALIDATION_LOG << "Use ComputeCommands for compute shader stages."; + case ShaderStage::kUnknown: + return false; + } + + return false; } bool Command::BindResource( @@ -36,14 +56,6 @@ bool Command::BindResource( const ShaderUniformSlot& slot, const std::shared_ptr& metadata, BufferView view) { - return DoBindResource(stage, slot, metadata, std::move(view)); -} - -template -bool Command::DoBindResource(ShaderStage stage, - const ShaderUniformSlot& slot, - const T metadata, - BufferView view) { FML_DCHECK(slot.ext_res_0 != VertexDescriptor::kReservedVertexBufferIndex); if (!view) { return false; @@ -52,11 +64,11 @@ bool Command::DoBindResource(ShaderStage stage, switch (stage) { case ShaderStage::kVertex: vertex_bindings.buffers.emplace_back(BufferAndUniformSlot{ - .slot = slot, .view = BufferResource(metadata, std::move(view))}); + .slot = slot, .view = BufferResource(*metadata, std::move(view))}); return true; case ShaderStage::kFragment: fragment_bindings.buffers.emplace_back(BufferAndUniformSlot{ - .slot = slot, .view = BufferResource(metadata, std::move(view))}); + .slot = slot, .view = BufferResource(*metadata, std::move(view))}); return true; case ShaderStage::kCompute: VALIDATION_LOG << "Use ComputeCommands for compute shader stages."; @@ -84,14 +96,14 @@ bool Command::BindResource(ShaderStage stage, case ShaderStage::kVertex: vertex_bindings.sampled_images.emplace_back(TextureAndSampler{ .slot = slot, - .texture = {&metadata, std::move(texture)}, + .texture = TextureResource(metadata, std::move(texture)), .sampler = sampler, }); return true; case ShaderStage::kFragment: fragment_bindings.sampled_images.emplace_back(TextureAndSampler{ .slot = slot, - .texture = {&metadata, std::move(texture)}, + .texture = TextureResource(metadata, std::move(texture)), .sampler = sampler, }); return true; diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index 5b08d1a531257..149efa1bea1ed 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -38,9 +38,9 @@ struct Resource { Resource(const ShaderMetadata* metadata, ResourceType p_resource) : resource(p_resource), metadata_(metadata) {} - Resource(std::shared_ptr& metadata, - ResourceType p_resource) - : resource(p_resource), dynamic_metadata_(metadata) {} + Resource(const ShaderMetadata& metadata, ResourceType p_resource) + : resource(p_resource), + dynamic_metadata_(std::make_shared(metadata)) {} const ShaderMetadata* GetMetadata() const { return dynamic_metadata_ ? dynamic_metadata_.get() : metadata_; @@ -51,7 +51,7 @@ struct Resource { const ShaderMetadata* metadata_ = nullptr; // Dynamically generated shader metadata. - std::shared_ptr dynamic_metadata_; + std::shared_ptr dynamic_metadata_ = nullptr; }; using BufferResource = Resource; From 15378eb28e52c78af6f32e137400149c894efa72 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 13 Aug 2024 12:25:07 -0700 Subject: [PATCH 22/38] ++ --- impeller/aiks/image_filter.cc | 21 +++++++++++++++------ lib/ui/painting.dart | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/impeller/aiks/image_filter.cc b/impeller/aiks/image_filter.cc index 46eea34ab102b..004d956651c06 100644 --- a/impeller/aiks/image_filter.cc +++ b/impeller/aiks/image_filter.cc @@ -155,7 +155,9 @@ std::shared_ptr MatrixImageFilter::Clone() const { return std::make_shared(*this); } -void MatrixImageFilter::Visit(ImageFilterVisitor& visitor) { visitor.Visit(*this); } +void MatrixImageFilter::Visit(ImageFilterVisitor& visitor) { + visitor.Visit(*this); +} /******************************************************************************* ******* ComposeImageFilter @@ -176,8 +178,9 @@ std::shared_ptr ComposeImageFilter::Clone() const { return std::make_shared(*this); } -void ComposeImageFilter::Visit(ImageFilterVisitor& visitor) { visitor.Visit(*this); } - +void ComposeImageFilter::Visit(ImageFilterVisitor& visitor) { + visitor.Visit(*this); +} /******************************************************************************* ******* ColorImageFilter @@ -198,7 +201,9 @@ std::shared_ptr ColorImageFilter::Clone() const { return std::make_shared(*this); } -void ColorImageFilter::Visit(ImageFilterVisitor& visitor) { visitor.Visit(*this); } +void ColorImageFilter::Visit(ImageFilterVisitor& visitor) { + visitor.Visit(*this); +} /******************************************************************************* ******* LocalMatrixImageFilter @@ -221,7 +226,9 @@ std::shared_ptr LocalMatrixImageFilter::Clone() const { return std::make_shared(*this); } -void LocalMatrixImageFilter::Visit(ImageFilterVisitor& visitor) { visitor.Visit(*this); } +void LocalMatrixImageFilter::Visit(ImageFilterVisitor& visitor) { + visitor.Visit(*this); +} /******************************************************************************* ******* RuntimeEffectImageFilter @@ -249,6 +256,8 @@ std::shared_ptr RuntimeEffectImageFilter::Clone() const { return std::make_shared(*this); } -void RuntimeEffectImageFilter::Visit(ImageFilterVisitor& visitor) { visitor.Visit(*this); } +void RuntimeEffectImageFilter::Visit(ImageFilterVisitor& visitor) { + visitor.Visit(*this); +} } // namespace impeller diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 3cbb4258fa92a..123bd359e9e22 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -3992,7 +3992,7 @@ class _FragmentShaderImageFilter implements ImageFilter { String get _shortDescription => 'shader'; @override - String toString() => 'ImageFilter.shader()'; + String toString() => 'ImageFilter.shader(Shader#${shader.hashCode})'; @override bool operator ==(Object other) { From fe023c7e8c6a2ba378cbd72ef3f9a2128684d459 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 15 Aug 2024 07:35:09 -0700 Subject: [PATCH 23/38] dont reuse color source for filter. --- display_list/effects/dl_image_filter.h | 42 +++++++++++++------ .../aiks_dl_runtime_effect_unittests.cc | 5 +-- impeller/display_list/dl_dispatcher.cc | 12 +++--- lib/ui/painting/fragment_program.cc | 8 ++++ lib/ui/painting/fragment_program.h | 5 +++ lib/ui/painting/fragment_shader.cc | 13 ++++++ lib/ui/painting/fragment_shader.h | 2 + lib/ui/painting/image_filter.cc | 3 +- 8 files changed, 66 insertions(+), 24 deletions(-) diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h index d3a7022e11db8..9edb5afe9c9dd 100644 --- a/display_list/effects/dl_image_filter.h +++ b/display_list/effects/dl_image_filter.h @@ -753,19 +753,25 @@ class DlLocalMatrixImageFilter final : public DlImageFilter { class DlRuntimeEffectImageFilter final : public DlImageFilter { public: explicit DlRuntimeEffectImageFilter( - std::shared_ptr runtime_effect) - : runtime_effect_(std::move(runtime_effect)) { - FML_DCHECK(!!runtime_effect_->asRuntimeEffect()); - } + sk_sp runtime_effect, + std::vector> samplers, + std::shared_ptr> uniform_data) + : runtime_effect_(std::move(runtime_effect)), + samplers_(std::move(samplers)), + uniform_data_(std::move(uniform_data)) {} std::shared_ptr shared() const override { - return std::make_shared(this->runtime_effect_); + return std::make_shared( + this->runtime_effect_, this->samplers_, this->uniform_data_); } - static std::shared_ptr Make( - std::shared_ptr runtime_effect) { + static std::shared_ptr Make( + sk_sp runtime_effect, + std::vector> samplers, + std::shared_ptr> uniform_data) { return std::make_shared( - std::move(runtime_effect)); + std::move(runtime_effect), std::move(samplers), + std::move(uniform_data)); } DlImageFilterType type() const override { @@ -775,10 +781,6 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { bool modifies_transparent_black() const override { return false; } - const std::shared_ptr& GetRuntimeEffect() const { - return runtime_effect_; - } - SkRect* map_local_bounds(const SkRect& input_bounds, SkRect& output_bounds) const override { output_bounds = input_bounds; @@ -803,11 +805,25 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { return this; } + const sk_sp runtime_effect() const { + return runtime_effect_; + } + + const std::vector>& samplers() const { + return samplers_; + } + + const std::shared_ptr>& uniform_data() const { + return uniform_data_; + } + protected: bool equals_(const DlImageFilter& other) const override { return false; } private: - std::shared_ptr runtime_effect_; + sk_sp runtime_effect_; + std::vector> samplers_; + std::shared_ptr> uniform_data_; }; } // namespace flutter diff --git a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc index bed95831348ca..d16364d7db617 100644 --- a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc +++ b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc @@ -104,9 +104,8 @@ TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { DlPaint paint; paint.setColor(DlColor::kAqua()); paint.setImageFilter(std::make_shared( - DlColorSource::MakeRuntimeEffect( - DlRuntimeEffect::MakeImpeller(runtime_stage), sampler_inputs, - uniform_data))); + DlRuntimeEffect::MakeImpeller(runtime_stage), sampler_inputs, + uniform_data)); DisplayListBuilder builder; builder.DrawRect(SkRect::MakeXYWH(0, 0, 400, 400), paint); diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 77dc66b523c7d..bfcd807803ab9 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -12,6 +12,7 @@ #include #include "display_list/dl_sampling_options.h" +#include "display_list/effects/dl_image_filter.h" #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" #include "impeller/aiks/color_filter.h" @@ -603,12 +604,11 @@ static std::shared_ptr ToImageFilter( case flutter::DlImageFilterType::kRuntimeEffect: auto fragment_program_filter = filter->asRuntimeEffectFilter(); FML_DCHECK(fragment_program_filter); - const flutter::DlRuntimeEffectColorSource* runtime_effect_color_source = - fragment_program_filter->GetRuntimeEffect()->asRuntimeEffect(); - auto runtime_stage = - runtime_effect_color_source->runtime_effect()->runtime_stage(); - auto uniform_data = runtime_effect_color_source->uniform_data(); - auto samplers = runtime_effect_color_source->samplers(); + const flutter::DlRuntimeEffectImageFilter* runtime_filter = + fragment_program_filter->asRuntimeEffectFilter(); + auto runtime_stage = runtime_filter->runtime_effect()->runtime_stage(); + const auto& uniform_data = runtime_filter->uniform_data(); + const auto& samplers = runtime_filter->samplers(); std::vector texture_inputs; size_t index = 0; diff --git a/lib/ui/painting/fragment_program.cc b/lib/ui/painting/fragment_program.cc index 6e63cbd6161ae..825bb84db6652 100644 --- a/lib/ui/painting/fragment_program.cc +++ b/lib/ui/painting/fragment_program.cc @@ -5,6 +5,7 @@ #include #include +#include "display_list/effects/dl_image_filter.h" #include "display_list/effects/dl_runtime_effect.h" #include "flutter/lib/ui/painting/fragment_program.h" @@ -144,6 +145,13 @@ std::shared_ptr FragmentProgram::MakeDlColorSource( std::move(float_uniforms)); } +std::shared_ptr FragmentProgram::MakeDlImageFilter( + std::shared_ptr> float_uniforms, + const std::vector>& children) { + return DlRuntimeEffectImageFilter::Make(runtime_effect_, children, + float_uniforms); +} + void FragmentProgram::Create(Dart_Handle wrapper) { auto res = fml::MakeRefCounted(); res->AssociateWithDartWrapper(wrapper); diff --git a/lib/ui/painting/fragment_program.h b/lib/ui/painting/fragment_program.h index 65d4d8809fd9a..65eb460658c51 100644 --- a/lib/ui/painting/fragment_program.h +++ b/lib/ui/painting/fragment_program.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_PAINTING_FRAGMENT_PROGRAM_H_ #define FLUTTER_LIB_UI_PAINTING_FRAGMENT_PROGRAM_H_ +#include "display_list/effects/dl_image_filter.h" #include "flutter/display_list/effects/dl_runtime_effect.h" #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/shader.h" @@ -38,6 +39,10 @@ class FragmentProgram : public RefCountedDartWrappable { std::shared_ptr> float_uniforms, const std::vector>& children); + std::shared_ptr MakeDlImageFilter( + std::shared_ptr> float_uniforms, + const std::vector>& children); + private: FragmentProgram(); sk_sp runtime_effect_; diff --git a/lib/ui/painting/fragment_shader.cc b/lib/ui/painting/fragment_shader.cc index 1300ab6f2d4b7..ecf67cafe7be7 100644 --- a/lib/ui/painting/fragment_shader.cc +++ b/lib/ui/painting/fragment_shader.cc @@ -86,6 +86,19 @@ void ReusableFragmentShader::SetImageSampler(Dart_Handle index_handle, uniform_floats[float_count_ + 2 * index + 1] = image->height(); } +std::shared_ptr ReusableFragmentShader::as_image_filter() const { + FML_CHECK(program_); + + // The lifetime of this object is longer than a frame, and the uniforms can be + // continually changed on the UI thread. So we take a copy of the uniforms + // before handing it to the DisplayList for consumption on the render thread. + auto uniform_data = std::make_shared>(); + uniform_data->resize(uniform_data_->size()); + memcpy(uniform_data->data(), uniform_data_->bytes(), uniform_data->size()); + + return program_->MakeDlImageFilter(std::move(uniform_data), samplers_); +} + std::shared_ptr ReusableFragmentShader::shader( DlImageSampling sampling) { FML_CHECK(program_); diff --git a/lib/ui/painting/fragment_shader.h b/lib/ui/painting/fragment_shader.h index a8a89ea816432..e82a22418156a 100644 --- a/lib/ui/painting/fragment_shader.h +++ b/lib/ui/painting/fragment_shader.h @@ -45,6 +45,8 @@ class ReusableFragmentShader : public Shader { // |Shader| std::shared_ptr shader(DlImageSampling) override; + std::shared_ptr as_image_filter() const; + private: ReusableFragmentShader(fml::RefPtr program, uint64_t float_count, diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index fb117a0839bc1..f9b6bf523e454 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -90,8 +90,7 @@ void ImageFilter::initComposeFilter(ImageFilter* outer, ImageFilter* inner) { void ImageFilter::initShader(ReusableFragmentShader* shader) { FML_DCHECK(shader); - filter_ = DlRuntimeEffectImageFilter::Make( - shader->shader(DlImageSampling::kNearestNeighbor)); + filter_ = shader->as_image_filter(); } } // namespace flutter From 65e85250b805f11c9d3eae8ae72b99dec05d25c7 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Thu, 15 Aug 2024 08:43:45 -0700 Subject: [PATCH 24/38] Update fragment_program.cc --- lib/ui/painting/fragment_program.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/painting/fragment_program.cc b/lib/ui/painting/fragment_program.cc index 825bb84db6652..2bb8b0e7a9cfb 100644 --- a/lib/ui/painting/fragment_program.cc +++ b/lib/ui/painting/fragment_program.cc @@ -149,7 +149,7 @@ std::shared_ptr FragmentProgram::MakeDlImageFilter( std::shared_ptr> float_uniforms, const std::vector>& children) { return DlRuntimeEffectImageFilter::Make(runtime_effect_, children, - float_uniforms); + std::move(float_uniforms)); } void FragmentProgram::Create(Dart_Handle wrapper) { From 0684931a7ab130b3d2624e7fde367102a32152d1 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 20 Aug 2024 17:34:23 -0700 Subject: [PATCH 25/38] update for dl testing. --- testing/display_list_testing.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing/display_list_testing.h b/testing/display_list_testing.h index 3ce9ec2bfe65e..ab935eae3817e 100644 --- a/testing/display_list_testing.h +++ b/testing/display_list_testing.h @@ -7,6 +7,7 @@ #include +#include "display_list/effects/dl_image_filter.h" #include "flutter/display_list/display_list.h" #include "flutter/display_list/dl_op_receiver.h" @@ -285,6 +286,7 @@ class DisplayListGeneralReceiver : public DlOpReceiver { case DlImageFilterType::kCompose: case DlImageFilterType::kLocalMatrix: case DlImageFilterType::kColorFilter: + case DlImageFilterType::kRuntimeEffect: RecordByType(DisplayListOpType::kSetSharedImageFilter); break; } From eba4796bf22c9fded78d41774683e6ffad452669 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 21 Aug 2024 12:49:20 -0700 Subject: [PATCH 26/38] Update impeller_golden_tests_output.txt --- testing/impeller_golden_tests_output.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/impeller_golden_tests_output.txt b/testing/impeller_golden_tests_output.txt index 1d9eb21f707ef..5293823d61b16 100644 --- a/testing/impeller_golden_tests_output.txt +++ b/testing/impeller_golden_tests_output.txt @@ -401,6 +401,7 @@ impeller_Play_AiksTest_CanRenderRoundedRectWithNonUniformRadii_Metal.png impeller_Play_AiksTest_CanRenderRoundedRectWithNonUniformRadii_OpenGLES.png impeller_Play_AiksTest_CanRenderRoundedRectWithNonUniformRadii_Vulkan.png impeller_Play_AiksTest_CanRenderRuntimeEffectFilter_Metal.png +impeller_Play_AiksTest_CanRenderRuntimeEffectFilter_OpenGLES.png impeller_Play_AiksTest_CanRenderRuntimeEffectFilter_Vulkan.png impeller_Play_AiksTest_CanRenderSimpleClips_Metal.png impeller_Play_AiksTest_CanRenderSimpleClips_OpenGLES.png From b6a8a232d2472f053175c510d74bf741a2686b0f Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Sat, 2 Nov 2024 12:21:21 -0700 Subject: [PATCH 27/38] ++ --- display_list/effects/dl_image_filter.h | 21 +++++- .../effects/dl_image_filter_unittests.cc | 65 +++++++++++++++++++ testing/display_list_testing.cc | 2 +- 3 files changed, 86 insertions(+), 2 deletions(-) diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h index cf83c40e8b47b..0aa85d0f6b3bc 100644 --- a/display_list/effects/dl_image_filter.h +++ b/display_list/effects/dl_image_filter.h @@ -819,7 +819,26 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { } protected: - bool equals_(const DlImageFilter& other) const override { return false; } + bool equals_(const DlImageFilter& other) const override { + FML_DCHECK(other.type() == DlImageFilterType::kRuntimeEffect); + auto that = static_cast(&other); + if (runtime_effect_ != that->runtime_effect_ || + samplers_.size() != that->samplers().size() || + uniform_data_->size() != that->uniform_data()->size()) { + return false; + } + for (auto i = 0u; i < samplers_.size(); i++) { + if (samplers_[i] != that->samplers()[i]) { + return false; + } + } + for (auto i = 0u; i < uniform_data_->size(); i++) { + if (uniform_data_->at(i) != that->uniform_data()->at(i)) { + return false; + } + } + return true; + } private: sk_sp runtime_effect_; diff --git a/display_list/effects/dl_image_filter_unittests.cc b/display_list/effects/dl_image_filter_unittests.cc index 70925455aa999..f3c46bc700786 100644 --- a/display_list/effects/dl_image_filter_unittests.cc +++ b/display_list/effects/dl_image_filter_unittests.cc @@ -12,6 +12,8 @@ #include "flutter/display_list/utils/dl_comparable.h" #include "gtest/gtest.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkRect.h" #include "third_party/skia/include/core/SkBlendMode.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSamplingOptions.h" @@ -823,5 +825,68 @@ TEST(DisplayListImageFilter, LocalImageFilterBounds) { } } +TEST(DisplayListImageFilter, RuntimeEffectEquality) { + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, + std::make_shared>()); + DlRuntimeEffectImageFilter filter_b(nullptr, {nullptr}, + std::make_shared>()); + + EXPECT_EQ(filter_a, filter_b); + + DlRuntimeEffectImageFilter filter_c( + nullptr, {nullptr}, std::make_shared>(1)); + + EXPECT_NE(filter_a, filter_c); +} + +TEST(DisplayListImageFilter, RuntimeEffectMapDeviceBounds) { + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, + std::make_shared>()); + + auto input_bounds = SkIRect::MakeLTRB(0, 0, 100, 100); + SkMatrix identity; + SkIRect output_bounds; + SkIRect* result = + filter_a.map_device_bounds(input_bounds, identity, output_bounds); + + EXPECT_NE(result, nullptr); + EXPECT_EQ(output_bounds, input_bounds); +} + +TEST(DisplayListImageFilter, RuntimeEffectMapInputBounds) { + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, + std::make_shared>()); + + auto input_bounds = SkRect::MakeLTRB(0, 0, 100, 100); + + SkRect output_bounds; + SkRect* result = filter_a.map_local_bounds(input_bounds, output_bounds); + + EXPECT_NE(result, nullptr); + EXPECT_EQ(output_bounds, input_bounds); +} + +TEST(DisplayListImageFilter, RuntimeEffectGetInputDeviceBounds) { + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, + std::make_shared>()); + + auto output_bounds = SkIRect::MakeLTRB(0, 0, 100, 100); + + SkMatrix identity; + SkIRect input_bounds; + SkIRect* result = + filter_a.get_input_device_bounds(output_bounds, identity, input_bounds); + + EXPECT_NE(result, nullptr); + EXPECT_EQ(output_bounds, input_bounds); +} + +TEST(DisplayListImageFilter, RuntimeEffectModifiesTransparentBlack) { + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, + std::make_shared>()); + + EXPECT_TRUE(filter_a.modifies_transparent_black()); +} + } // namespace testing } // namespace flutter diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index 58ce6e0a2ce28..88bd51d6e2ea6 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -7,8 +7,8 @@ #include #include -#include "display_list/effects/dl_image_filter.h" #include "flutter/display_list/display_list.h" +#include "flutter/display_list/effects/dl_image_filter.h" namespace flutter { namespace testing { From d61c68c2df05dd787635ab02da776e6976e6aa3e Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Sat, 2 Nov 2024 12:23:10 -0700 Subject: [PATCH 28/38] ++ --- display_list/effects/dl_image_filter_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/display_list/effects/dl_image_filter_unittests.cc b/display_list/effects/dl_image_filter_unittests.cc index f3c46bc700786..842374b9d332c 100644 --- a/display_list/effects/dl_image_filter_unittests.cc +++ b/display_list/effects/dl_image_filter_unittests.cc @@ -885,7 +885,7 @@ TEST(DisplayListImageFilter, RuntimeEffectModifiesTransparentBlack) { DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, std::make_shared>()); - EXPECT_TRUE(filter_a.modifies_transparent_black()); + EXPECT_FALSE(filter_a.modifies_transparent_black()); } } // namespace testing From bc0a43602ae5ee34c7e5341fb9780eb1080b7984 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 09:45:39 -0800 Subject: [PATCH 29/38] start adding matrix support. --- display_list/effects/dl_image_filter.h | 49 +++++++++++++++++++++----- lib/ui/painting.dart | 23 +++++++++--- lib/ui/painting/fragment_program.cc | 5 +-- lib/ui/painting/fragment_program.h | 4 ++- lib/ui/painting/fragment_shader.cc | 6 ++-- lib/ui/painting/fragment_shader.h | 2 +- lib/ui/painting/image_filter.cc | 4 +-- lib/ui/painting/image_filter.h | 2 +- 8 files changed, 72 insertions(+), 23 deletions(-) diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h index 0aa85d0f6b3bc..18f8864ef5104 100644 --- a/display_list/effects/dl_image_filter.h +++ b/display_list/effects/dl_image_filter.h @@ -15,6 +15,7 @@ #include "flutter/display_list/utils/dl_comparable.h" #include "flutter/fml/logging.h" +#include "include/core/SkM44.h" #include "third_party/skia/include/core/SkMatrix.h" namespace flutter { @@ -756,23 +757,26 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { explicit DlRuntimeEffectImageFilter( sk_sp runtime_effect, std::vector> samplers, - std::shared_ptr> uniform_data) + std::shared_ptr> uniform_data, + const SkMatrix& matrix) : runtime_effect_(std::move(runtime_effect)), samplers_(std::move(samplers)), - uniform_data_(std::move(uniform_data)) {} + uniform_data_(std::move(uniform_data)), + matrix_(matrix) {} std::shared_ptr shared() const override { return std::make_shared( - this->runtime_effect_, this->samplers_, this->uniform_data_); + this->runtime_effect_, this->samplers_, this->uniform_data_, this->matrix_); } static std::shared_ptr Make( sk_sp runtime_effect, std::vector> samplers, - std::shared_ptr> uniform_data) { + std::shared_ptr> uniform_data, + const SkMatrix& matrix) { return std::make_shared( std::move(runtime_effect), std::move(samplers), - std::move(uniform_data)); + std::move(uniform_data), matrix); } DlImageFilterType type() const override { @@ -782,23 +786,42 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { bool modifies_transparent_black() const override { return false; } - SkRect* map_local_bounds(const SkRect& input_bounds, + SkRect* map_local_bounds(const SkRect& input_bounds, SkRect& output_bounds) const override { - output_bounds = input_bounds; + output_bounds = matrix_.mapRect(input_bounds); return &output_bounds; } SkIRect* map_device_bounds(const SkIRect& input_bounds, const SkMatrix& ctm, SkIRect& output_bounds) const override { - output_bounds = input_bounds; + SkMatrix matrix; + if (!ctm.invert(&matrix)) { + output_bounds = input_bounds; + return nullptr; + } + matrix.postConcat(matrix_); + matrix.postConcat(ctm); + SkRect device_rect; + matrix.mapRect(&device_rect, SkRect::Make(input_bounds)); + output_bounds = device_rect.roundOut(); return &output_bounds; } SkIRect* get_input_device_bounds(const SkIRect& output_bounds, const SkMatrix& ctm, SkIRect& input_bounds) const override { - input_bounds = output_bounds; + SkMatrix matrix = SkMatrix::Concat(ctm, matrix_); + SkMatrix inverse; + if (!matrix.invert(&inverse)) { + input_bounds = output_bounds; + return nullptr; + } + inverse.postConcat(ctm); + SkRect bounds; + bounds.set(output_bounds); + inverse.mapRect(&bounds); + input_bounds = bounds.roundOut(); return &input_bounds; } @@ -818,6 +841,10 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { return uniform_data_; } + const SkMatrix& matrix() const { + return matrix_; + } + protected: bool equals_(const DlImageFilter& other) const override { FML_DCHECK(other.type() == DlImageFilterType::kRuntimeEffect); @@ -837,6 +864,9 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { return false; } } + if (matrix_ != that->matrix_) { + return false; + } return true; } @@ -844,6 +874,7 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { sk_sp runtime_effect_; std::vector> samplers_; std::shared_ptr> uniform_data_; + SkMatrix matrix_; }; } // namespace flutter diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index ce94cb58a018c..4125a92e384ec 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -4077,7 +4077,7 @@ abstract class ImageFilter { /// This API is only supported when using the Impeller rendering engine. On /// other backends a [UnsupportedError] will be thrown. To check at runtime /// whether this API is suppored use [isShaderFilterSupported]. - factory ImageFilter.shader(FragmentShader shader) { + factory ImageFilter.shader(FragmentShader shader, { Float64List? matrix4 }) { if (!_impellerEnabled) { throw UnsupportedError('ImageFilter.shader only supported with Impeller rendering engine.'); } @@ -4086,7 +4086,18 @@ abstract class ImageFilter { 'ImageFilter.shader requires that the first uniform is a ' 'vec2 and at least one sampler uniform is present.'); } - return _FragmentShaderImageFilter(shader); + if (matrix4 != null && matrix4.length != 16) { + throw ArgumentError.value(matrix4, 'matrix4', 'matrix4" must have 16 entries.'); + } + if (matrix4 == null) { + final Float64List identity = Float64List(16); + identity[0] = 1.0; + identity[5] = 1.0; + identity[10] = 1.0; + identity[15] = 1.0; + matrix4 = identity; + } + return _FragmentShaderImageFilter(shader, matrix4); } /// Whether [ImageFilter.shader] is supported on the current backend. @@ -4266,9 +4277,10 @@ class _ComposeImageFilter implements ImageFilter { } class _FragmentShaderImageFilter implements ImageFilter { - _FragmentShaderImageFilter(this.shader); + _FragmentShaderImageFilter(this.shader, this.matrix4); final FragmentShader shader; + final Float64List matrix4; late final _ImageFilter nativeFilter = _ImageFilter.shader(this); @@ -4279,7 +4291,7 @@ class _FragmentShaderImageFilter implements ImageFilter { String get _shortDescription => 'shader'; @override - String toString() => 'ImageFilter.shader(Shader#${shader.hashCode})'; + String toString() => 'ImageFilter.shader(Shader#${shader.hashCode}, $matrix4)'; @override bool operator ==(Object other) { @@ -4287,7 +4299,8 @@ class _FragmentShaderImageFilter implements ImageFilter { return false; } return other is _FragmentShaderImageFilter - && other.shader == shader; + && other.shader == shader + && _listEquals(other.matrix4, matrix4); } @override diff --git a/lib/ui/painting/fragment_program.cc b/lib/ui/painting/fragment_program.cc index 2bb8b0e7a9cfb..2a88bf01e5ce8 100644 --- a/lib/ui/painting/fragment_program.cc +++ b/lib/ui/painting/fragment_program.cc @@ -147,9 +147,10 @@ std::shared_ptr FragmentProgram::MakeDlColorSource( std::shared_ptr FragmentProgram::MakeDlImageFilter( std::shared_ptr> float_uniforms, - const std::vector>& children) { + const std::vector>& children, + const SkMatrix& matrix) { return DlRuntimeEffectImageFilter::Make(runtime_effect_, children, - std::move(float_uniforms)); + std::move(float_uniforms), matrix); } void FragmentProgram::Create(Dart_Handle wrapper) { diff --git a/lib/ui/painting/fragment_program.h b/lib/ui/painting/fragment_program.h index 65eb460658c51..1703edae3fe5a 100644 --- a/lib/ui/painting/fragment_program.h +++ b/lib/ui/painting/fragment_program.h @@ -10,6 +10,7 @@ #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/shader.h" +#include "include/core/SkMatrix.h" #include "third_party/tonic/dart_library_natives.h" #include "third_party/tonic/typed_data/typed_list.h" @@ -41,7 +42,8 @@ class FragmentProgram : public RefCountedDartWrappable { std::shared_ptr MakeDlImageFilter( std::shared_ptr> float_uniforms, - const std::vector>& children); + const std::vector>& children, + const SkMatrix& matrix); private: FragmentProgram(); diff --git a/lib/ui/painting/fragment_shader.cc b/lib/ui/painting/fragment_shader.cc index ecf67cafe7be7..aa85d049186b7 100644 --- a/lib/ui/painting/fragment_shader.cc +++ b/lib/ui/painting/fragment_shader.cc @@ -86,7 +86,8 @@ void ReusableFragmentShader::SetImageSampler(Dart_Handle index_handle, uniform_floats[float_count_ + 2 * index + 1] = image->height(); } -std::shared_ptr ReusableFragmentShader::as_image_filter() const { +std::shared_ptr ReusableFragmentShader::as_image_filter( + const SkMatrix& matrix) const { FML_CHECK(program_); // The lifetime of this object is longer than a frame, and the uniforms can be @@ -96,7 +97,8 @@ std::shared_ptr ReusableFragmentShader::as_image_filter() const { uniform_data->resize(uniform_data_->size()); memcpy(uniform_data->data(), uniform_data_->bytes(), uniform_data->size()); - return program_->MakeDlImageFilter(std::move(uniform_data), samplers_); + return program_->MakeDlImageFilter(std::move(uniform_data), samplers_, + matrix); } std::shared_ptr ReusableFragmentShader::shader( diff --git a/lib/ui/painting/fragment_shader.h b/lib/ui/painting/fragment_shader.h index e82a22418156a..0480119a0b4ba 100644 --- a/lib/ui/painting/fragment_shader.h +++ b/lib/ui/painting/fragment_shader.h @@ -45,7 +45,7 @@ class ReusableFragmentShader : public Shader { // |Shader| std::shared_ptr shader(DlImageSampling) override; - std::shared_ptr as_image_filter() const; + std::shared_ptr as_image_filter(const SkMatrix& matrix) const; private: ReusableFragmentShader(fml::RefPtr program, diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index f9b6bf523e454..784b11d7b8c61 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -88,9 +88,9 @@ void ImageFilter::initComposeFilter(ImageFilter* outer, ImageFilter* inner) { filter_ = DlComposeImageFilter::Make(outer->filter(), inner->filter()); } -void ImageFilter::initShader(ReusableFragmentShader* shader) { +void ImageFilter::initShader(ReusableFragmentShader* shader, const tonic::Float64List& matrix4) { FML_DCHECK(shader); - filter_ = shader->as_image_filter(); + filter_ = shader->as_image_filter(ToSkMatrix(matrix4)); } } // namespace flutter diff --git a/lib/ui/painting/image_filter.h b/lib/ui/painting/image_filter.h index 1ca29dad712ec..bd45baa656e03 100644 --- a/lib/ui/painting/image_filter.h +++ b/lib/ui/painting/image_filter.h @@ -35,7 +35,7 @@ class ImageFilter : public RefCountedDartWrappable { void initMatrix(const tonic::Float64List& matrix4, int filter_quality_index); void initColorFilter(ColorFilter* colorFilter); void initComposeFilter(ImageFilter* outer, ImageFilter* inner); - void initShader(ReusableFragmentShader* shader); + void initShader(ReusableFragmentShader* shader, const tonic::Float64List& matrix4); const std::shared_ptr filter() const { return filter_; } From 933a4cec55e6b0cf27ecf157ceb15e705f75b1c1 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 10:46:50 -0800 Subject: [PATCH 30/38] maybe its ok --- display_list/effects/dl_image_filter.h | 13 ++- .../effects/dl_image_filter_unittests.cc | 29 +++---- .../aiks_dl_runtime_effect_unittests.cc | 30 ++++++- impeller/display_list/image_filter.cc | 7 +- .../contents/filters/filter_contents.cc | 26 +++++- .../entity/contents/filters/filter_contents.h | 8 +- .../filters/matrix_filter_contents.cc | 22 ------ .../filters/runtime_effect_filter_contents.cc | 79 ++++++++++++++++--- .../filters/runtime_effect_filter_contents.h | 13 +++ lib/ui/painting/image_filter.cc | 3 +- lib/ui/painting/image_filter.h | 3 +- 11 files changed, 173 insertions(+), 60 deletions(-) diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h index 18f8864ef5104..94d849ef1b8cf 100644 --- a/display_list/effects/dl_image_filter.h +++ b/display_list/effects/dl_image_filter.h @@ -766,7 +766,8 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { std::shared_ptr shared() const override { return std::make_shared( - this->runtime_effect_, this->samplers_, this->uniform_data_, this->matrix_); + this->runtime_effect_, this->samplers_, this->uniform_data_, + this->matrix_); } static std::shared_ptr Make( @@ -775,8 +776,8 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { std::shared_ptr> uniform_data, const SkMatrix& matrix) { return std::make_shared( - std::move(runtime_effect), std::move(samplers), - std::move(uniform_data), matrix); + std::move(runtime_effect), std::move(samplers), std::move(uniform_data), + matrix); } DlImageFilterType type() const override { @@ -786,7 +787,7 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { bool modifies_transparent_black() const override { return false; } - SkRect* map_local_bounds(const SkRect& input_bounds, + SkRect* map_local_bounds(const SkRect& input_bounds, SkRect& output_bounds) const override { output_bounds = matrix_.mapRect(input_bounds); return &output_bounds; @@ -841,9 +842,7 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { return uniform_data_; } - const SkMatrix& matrix() const { - return matrix_; - } + const SkMatrix& matrix() const { return matrix_; } protected: bool equals_(const DlImageFilter& other) const override { diff --git a/display_list/effects/dl_image_filter_unittests.cc b/display_list/effects/dl_image_filter_unittests.cc index 842374b9d332c..3ba06bbcec887 100644 --- a/display_list/effects/dl_image_filter_unittests.cc +++ b/display_list/effects/dl_image_filter_unittests.cc @@ -826,22 +826,23 @@ TEST(DisplayListImageFilter, LocalImageFilterBounds) { } TEST(DisplayListImageFilter, RuntimeEffectEquality) { - DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, - std::make_shared>()); - DlRuntimeEffectImageFilter filter_b(nullptr, {nullptr}, - std::make_shared>()); + DlRuntimeEffectImageFilter filter_a( + nullptr, {nullptr}, std::make_shared>(), SkMatrix()); + DlRuntimeEffectImageFilter filter_b( + nullptr, {nullptr}, std::make_shared>(), SkMatrix()); EXPECT_EQ(filter_a, filter_b); - DlRuntimeEffectImageFilter filter_c( - nullptr, {nullptr}, std::make_shared>(1)); + DlRuntimeEffectImageFilter filter_c(nullptr, {nullptr}, + std::make_shared>(1), + SkMatrix()); EXPECT_NE(filter_a, filter_c); } TEST(DisplayListImageFilter, RuntimeEffectMapDeviceBounds) { - DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, - std::make_shared>()); + DlRuntimeEffectImageFilter filter_a( + nullptr, {nullptr}, std::make_shared>(), SkMatrix()); auto input_bounds = SkIRect::MakeLTRB(0, 0, 100, 100); SkMatrix identity; @@ -854,8 +855,8 @@ TEST(DisplayListImageFilter, RuntimeEffectMapDeviceBounds) { } TEST(DisplayListImageFilter, RuntimeEffectMapInputBounds) { - DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, - std::make_shared>()); + DlRuntimeEffectImageFilter filter_a( + nullptr, {nullptr}, std::make_shared>(), SkMatrix()); auto input_bounds = SkRect::MakeLTRB(0, 0, 100, 100); @@ -867,8 +868,8 @@ TEST(DisplayListImageFilter, RuntimeEffectMapInputBounds) { } TEST(DisplayListImageFilter, RuntimeEffectGetInputDeviceBounds) { - DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, - std::make_shared>()); + DlRuntimeEffectImageFilter filter_a( + nullptr, {nullptr}, std::make_shared>(), SkMatrix()); auto output_bounds = SkIRect::MakeLTRB(0, 0, 100, 100); @@ -882,8 +883,8 @@ TEST(DisplayListImageFilter, RuntimeEffectGetInputDeviceBounds) { } TEST(DisplayListImageFilter, RuntimeEffectModifiesTransparentBlack) { - DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, - std::make_shared>()); + DlRuntimeEffectImageFilter filter_a( + nullptr, {nullptr}, std::make_shared>(), SkMatrix()); EXPECT_FALSE(filter_a.modifies_transparent_black()); } diff --git a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc index 49017b497240e..a47c9ef008a18 100644 --- a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc +++ b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc @@ -11,6 +11,7 @@ #include "flutter/display_list/effects/dl_runtime_effect.h" #include "flutter/impeller/display_list/aiks_unittests.h" +#include "include/core/SkMatrix.h" #include "include/core/SkPath.h" #include "include/core/SkRRect.h" @@ -103,7 +104,34 @@ TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { paint.setColor(DlColor::kAqua()); paint.setImageFilter(std::make_shared( DlRuntimeEffect::MakeImpeller(runtime_stage), sampler_inputs, - uniform_data)); + uniform_data, SkMatrix())); + + DisplayListBuilder builder; + builder.DrawRect(SkRect::MakeXYWH(0, 0, 400, 400), paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CanRenderRuntimeEffectFilterWithTransform) { + auto runtime_stages = + OpenAssetAsRuntimeStage("runtime_stage_filter_example.frag.iplr"); + + auto runtime_stage = + runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())]; + ASSERT_TRUE(runtime_stage); + ASSERT_TRUE(runtime_stage->IsDirty()); + + std::vector> sampler_inputs = { + nullptr, + }; + auto uniform_data = std::make_shared>(); + uniform_data->resize(sizeof(Vector2)); + + DlPaint paint; + paint.setColor(DlColor::kAqua()); + paint.setImageFilter(std::make_shared( + DlRuntimeEffect::MakeImpeller(runtime_stage), sampler_inputs, + uniform_data, SkMatrix::Scale(0.5, 0.5))); DisplayListBuilder builder; builder.DrawRect(SkRect::MakeXYWH(0, 0, 400, 400), paint); diff --git a/impeller/display_list/image_filter.cc b/impeller/display_list/image_filter.cc index e1cb1e00cd905..59890abde4aa6 100644 --- a/impeller/display_list/image_filter.cc +++ b/impeller/display_list/image_filter.cc @@ -137,9 +137,10 @@ std::shared_ptr WrapInput(const flutter::DlImageFilter* filter, .texture = image->image()->impeller_texture(), }); } - return FilterContents::MakeRuntimeEffect(input, std::move(runtime_stage), - uniform_data, - std::move(texture_inputs)); + return FilterContents::MakeRuntimeEffect( + input, std::move(runtime_stage), uniform_data, + std::move(texture_inputs), + skia_conversions::ToMatrix(runtime_filter->matrix())); } } FML_UNREACHABLE(); diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index ddfaa2c6388c5..92d17151b196a 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -35,6 +35,28 @@ namespace impeller { const int32_t FilterContents::kBlurFilterRequiredMipCount = GaussianBlurFilterContents::kBlurFilterRequiredMipCount; +// static +Matrix FilterContents::CalculateSubpassTransform( + const Matrix& snapshot_transform, + const Matrix& effect_transform, + const Matrix& matrix, + Entity::RenderingMode rendering_mode) { + if (rendering_mode == + Entity::RenderingMode::kSubpassAppendSnapshotTransform) { + return snapshot_transform * // + effect_transform * // + matrix * // + effect_transform.Invert(); + } else { + FML_DCHECK(rendering_mode == + Entity::RenderingMode::kSubpassPrependSnapshotTransform); + return effect_transform * // + matrix * // + effect_transform.Invert() * // + snapshot_transform; + } +} + std::shared_ptr FilterContents::MakeGaussianBlur( const FilterInput::Ref& input, Sigma sigma_x, @@ -120,12 +142,14 @@ std::shared_ptr FilterContents::MakeRuntimeEffect( FilterInput::Ref input, std::shared_ptr runtime_stage, std::shared_ptr> uniforms, - std::vector texture_inputs) { + std::vector texture_inputs, + Matrix matrix) { auto filter = std::make_shared(); filter->SetInputs({std::move(input)}); filter->SetRuntimeStage(std::move(runtime_stage)); filter->SetUniforms(std::move(uniforms)); filter->SetTextureInputs(std::move(texture_inputs)); + filter->SetMatrix(matrix); return filter; } diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 3177cf7518ee3..c9380673318b2 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -38,6 +38,11 @@ class FilterContents : public Contents { enum class MorphType { kDilate, kErode }; + static Matrix CalculateSubpassTransform(const Matrix& snapshot_transform, + const Matrix& effect_transform, + const Matrix& matrix, + Entity::RenderingMode rendering_mode); + /// Creates a gaussian blur that operates in 2 dimensions. /// See also: `MakeDirectionalGaussianBlur` static std::shared_ptr MakeGaussianBlur( @@ -83,7 +88,8 @@ class FilterContents : public Contents { FilterInput::Ref input, std::shared_ptr runtime_stage, std::shared_ptr> uniforms, - std::vector texture_inputs); + std::vector texture_inputs, + Matrix matrix = Matrix()); FilterContents(); diff --git a/impeller/entity/contents/filters/matrix_filter_contents.cc b/impeller/entity/contents/filters/matrix_filter_contents.cc index 289e67fc922cb..7bee41f7b56c7 100644 --- a/impeller/entity/contents/filters/matrix_filter_contents.cc +++ b/impeller/entity/contents/filters/matrix_filter_contents.cc @@ -24,28 +24,6 @@ void MatrixFilterContents::SetSamplerDescriptor(SamplerDescriptor desc) { sampler_descriptor_ = std::move(desc); } -namespace { -Matrix CalculateSubpassTransform(const Matrix& snapshot_transform, - const Matrix& effect_transform, - const Matrix& matrix, - Entity::RenderingMode rendering_mode) { - if (rendering_mode == - Entity::RenderingMode::kSubpassAppendSnapshotTransform) { - return snapshot_transform * // - effect_transform * // - matrix * // - effect_transform.Invert(); - } else { - FML_DCHECK(rendering_mode == - Entity::RenderingMode::kSubpassPrependSnapshotTransform); - return effect_transform * // - matrix * // - effect_transform.Invert() * // - snapshot_transform; - } -} -} // namespace - std::optional MatrixFilterContents::RenderFilter( const FilterInput::Vector& inputs, const ContentContext& renderer, diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc index 4bc87c443efd9..afbeb54d7cc43 100644 --- a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc @@ -29,6 +29,10 @@ void RuntimeEffectFilterContents::SetTextureInputs( texture_inputs_ = std::move(texture_inputs); } +void RuntimeEffectFilterContents::SetMatrix(const Matrix& matrix) { + matrix_ = matrix; +} + // |FilterContents| std::optional RuntimeEffectFilterContents::RenderFilter( const FilterInput::Vector& inputs, @@ -41,12 +45,26 @@ std::optional RuntimeEffectFilterContents::RenderFilter( return std::nullopt; } - auto input_snapshot = - inputs[0]->GetSnapshot("RuntimeEffectContents", renderer, entity); - if (!input_snapshot.has_value()) { + auto snapshot = inputs[0]->GetSnapshot("Matrix", renderer, entity); + if (!snapshot.has_value()) { return std::nullopt; } - std::optional maybe_input_coverage = input_snapshot->GetCoverage(); + + if (rendering_mode_ == + Entity::RenderingMode::kSubpassPrependSnapshotTransform || + rendering_mode_ == + Entity::RenderingMode::kSubpassAppendSnapshotTransform) { + // See Note in MatrixFilterContents::RenderFilter + snapshot->transform = CalculateSubpassTransform( + snapshot->transform, effect_transform, matrix_, rendering_mode_); + } else { + snapshot->transform = entity.GetTransform() * // + matrix_ * // + entity.GetTransform().Invert() * // + snapshot->transform; + } + + std::optional maybe_input_coverage = snapshot->GetCoverage(); if (!maybe_input_coverage.has_value()) { return std::nullopt; } @@ -65,16 +83,17 @@ std::optional RuntimeEffectFilterContents::RenderFilter( // Update uniform values. std::vector texture_input_copy = texture_inputs_; - texture_input_copy[0].texture = input_snapshot->texture; + texture_input_copy[0].texture = snapshot->texture; - Size size = Size(input_snapshot->texture->GetSize()); + Size size = Size(snapshot->texture->GetSize()); + FML_LOG(ERROR) << "Size: " << size; memcpy(uniforms_->data(), &size, sizeof(Size)); //---------------------------------------------------------------------------- /// Create AnonymousContents for rendering. /// RenderProc render_proc = - [input_snapshot, runtime_stage = runtime_stage_, uniforms = uniforms_, + [snapshot, runtime_stage = runtime_stage_, uniforms = uniforms_, texture_inputs = texture_input_copy, input_coverage](const ContentContext& renderer, const Entity& entity, RenderPass& pass) -> bool { @@ -97,15 +116,57 @@ std::optional RuntimeEffectFilterContents::RenderFilter( Entity sub_entity; sub_entity.SetContents(std::move(contents)); sub_entity.SetBlendMode(entity.GetBlendMode()); - sub_entity.SetTransform(input_snapshot->transform); + sub_entity.SetTransform(snapshot->transform); return sub_entity; } +void RuntimeEffectFilterContents::SetRenderingMode( + Entity::RenderingMode rendering_mode) { + rendering_mode_ = rendering_mode; +} + +std::optional RuntimeEffectFilterContents::GetFilterCoverage( + const FilterInput::Vector& inputs, + const Entity& entity, + const Matrix& effect_transform) const { + if (inputs.empty()) { + return std::nullopt; + } + + std::optional coverage = inputs[0]->GetCoverage(entity); + if (!coverage.has_value()) { + return std::nullopt; + } + + Matrix input_transform = inputs[0]->GetTransform(entity); + if (rendering_mode_ == + Entity::RenderingMode::kSubpassPrependSnapshotTransform || + rendering_mode_ == + Entity::RenderingMode::kSubpassAppendSnapshotTransform) { + Rect coverage_bounds = coverage->TransformBounds(input_transform.Invert()); + Matrix transform = CalculateSubpassTransform( + input_transform, effect_transform, matrix_, rendering_mode_); + return coverage_bounds.TransformBounds(transform); + } else { + Matrix transform = input_transform * // + matrix_ * // + input_transform.Invert(); // + return coverage->TransformBounds(transform); + } +} + // |FilterContents| std::optional RuntimeEffectFilterContents::GetFilterSourceCoverage( const Matrix& effect_transform, const Rect& output_limit) const { - return output_limit; + auto transform = effect_transform * // + matrix_ * // + effect_transform.Invert(); // + if (transform.GetDeterminant() == 0.0) { + return std::nullopt; + } + auto inverse = transform.Invert(); + return output_limit.TransformBounds(inverse); } } // namespace impeller diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.h b/impeller/entity/contents/filters/runtime_effect_filter_contents.h index 3a68ecd67ab15..aa72463e7fd76 100644 --- a/impeller/entity/contents/filters/runtime_effect_filter_contents.h +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.h @@ -23,10 +23,23 @@ class RuntimeEffectFilterContents final : public FilterContents { void SetTextureInputs( std::vector texture_inputs); + void SetMatrix(const Matrix& matrix); + + // |FilterContents| + void SetRenderingMode(Entity::RenderingMode rendering_mode) override; + + // |FilterContents| + std::optional GetFilterCoverage( + const FilterInput::Vector& inputs, + const Entity& entity, + const Matrix& effect_transform) const override; + private: std::shared_ptr runtime_stage_; std::shared_ptr> uniforms_; std::vector texture_inputs_; + Matrix matrix_; + Entity::RenderingMode rendering_mode_ = Entity::RenderingMode::kDirect; // |FilterContents| std::optional RenderFilter( diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index f2ddb86dce296..5dfb7f09447f8 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -120,7 +120,8 @@ void ImageFilter::initComposeFilter(ImageFilter* outer, ImageFilter* inner) { inner->filter(DlTileMode::kClamp)); } -void ImageFilter::initShader(ReusableFragmentShader* shader, const tonic::Float64List& matrix4) { +void ImageFilter::initShader(ReusableFragmentShader* shader, + const tonic::Float64List& matrix4) { FML_DCHECK(shader); filter_ = shader->as_image_filter(ToSkMatrix(matrix4)); } diff --git a/lib/ui/painting/image_filter.h b/lib/ui/painting/image_filter.h index 14ab226c918da..84a50a6c8ce9c 100644 --- a/lib/ui/painting/image_filter.h +++ b/lib/ui/painting/image_filter.h @@ -35,7 +35,8 @@ class ImageFilter : public RefCountedDartWrappable { void initMatrix(const tonic::Float64List& matrix4, int filter_quality_index); void initColorFilter(ColorFilter* colorFilter); void initComposeFilter(ImageFilter* outer, ImageFilter* inner); - void initShader(ReusableFragmentShader* shader, const tonic::Float64List& matrix4); + void initShader(ReusableFragmentShader* shader, + const tonic::Float64List& matrix4); const std::shared_ptr filter(DlTileMode mode) const; From 8b8b6eea3f193c0ede49468ca7c3cd703165a655 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 15:03:25 -0800 Subject: [PATCH 31/38] Revert "maybe its ok" This reverts commit 933a4cec55e6b0cf27ecf157ceb15e705f75b1c1. --- display_list/effects/dl_image_filter.h | 13 +-- .../effects/dl_image_filter_unittests.cc | 29 ++++--- .../aiks_dl_runtime_effect_unittests.cc | 30 +------ impeller/display_list/image_filter.cc | 7 +- .../contents/filters/filter_contents.cc | 26 +----- .../entity/contents/filters/filter_contents.h | 8 +- .../filters/matrix_filter_contents.cc | 22 ++++++ .../filters/runtime_effect_filter_contents.cc | 79 +++---------------- .../filters/runtime_effect_filter_contents.h | 13 --- lib/ui/painting/image_filter.cc | 3 +- lib/ui/painting/image_filter.h | 3 +- 11 files changed, 60 insertions(+), 173 deletions(-) diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h index 94d849ef1b8cf..18f8864ef5104 100644 --- a/display_list/effects/dl_image_filter.h +++ b/display_list/effects/dl_image_filter.h @@ -766,8 +766,7 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { std::shared_ptr shared() const override { return std::make_shared( - this->runtime_effect_, this->samplers_, this->uniform_data_, - this->matrix_); + this->runtime_effect_, this->samplers_, this->uniform_data_, this->matrix_); } static std::shared_ptr Make( @@ -776,8 +775,8 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { std::shared_ptr> uniform_data, const SkMatrix& matrix) { return std::make_shared( - std::move(runtime_effect), std::move(samplers), std::move(uniform_data), - matrix); + std::move(runtime_effect), std::move(samplers), + std::move(uniform_data), matrix); } DlImageFilterType type() const override { @@ -787,7 +786,7 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { bool modifies_transparent_black() const override { return false; } - SkRect* map_local_bounds(const SkRect& input_bounds, + SkRect* map_local_bounds(const SkRect& input_bounds, SkRect& output_bounds) const override { output_bounds = matrix_.mapRect(input_bounds); return &output_bounds; @@ -842,7 +841,9 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { return uniform_data_; } - const SkMatrix& matrix() const { return matrix_; } + const SkMatrix& matrix() const { + return matrix_; + } protected: bool equals_(const DlImageFilter& other) const override { diff --git a/display_list/effects/dl_image_filter_unittests.cc b/display_list/effects/dl_image_filter_unittests.cc index 3ba06bbcec887..842374b9d332c 100644 --- a/display_list/effects/dl_image_filter_unittests.cc +++ b/display_list/effects/dl_image_filter_unittests.cc @@ -826,23 +826,22 @@ TEST(DisplayListImageFilter, LocalImageFilterBounds) { } TEST(DisplayListImageFilter, RuntimeEffectEquality) { - DlRuntimeEffectImageFilter filter_a( - nullptr, {nullptr}, std::make_shared>(), SkMatrix()); - DlRuntimeEffectImageFilter filter_b( - nullptr, {nullptr}, std::make_shared>(), SkMatrix()); + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, + std::make_shared>()); + DlRuntimeEffectImageFilter filter_b(nullptr, {nullptr}, + std::make_shared>()); EXPECT_EQ(filter_a, filter_b); - DlRuntimeEffectImageFilter filter_c(nullptr, {nullptr}, - std::make_shared>(1), - SkMatrix()); + DlRuntimeEffectImageFilter filter_c( + nullptr, {nullptr}, std::make_shared>(1)); EXPECT_NE(filter_a, filter_c); } TEST(DisplayListImageFilter, RuntimeEffectMapDeviceBounds) { - DlRuntimeEffectImageFilter filter_a( - nullptr, {nullptr}, std::make_shared>(), SkMatrix()); + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, + std::make_shared>()); auto input_bounds = SkIRect::MakeLTRB(0, 0, 100, 100); SkMatrix identity; @@ -855,8 +854,8 @@ TEST(DisplayListImageFilter, RuntimeEffectMapDeviceBounds) { } TEST(DisplayListImageFilter, RuntimeEffectMapInputBounds) { - DlRuntimeEffectImageFilter filter_a( - nullptr, {nullptr}, std::make_shared>(), SkMatrix()); + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, + std::make_shared>()); auto input_bounds = SkRect::MakeLTRB(0, 0, 100, 100); @@ -868,8 +867,8 @@ TEST(DisplayListImageFilter, RuntimeEffectMapInputBounds) { } TEST(DisplayListImageFilter, RuntimeEffectGetInputDeviceBounds) { - DlRuntimeEffectImageFilter filter_a( - nullptr, {nullptr}, std::make_shared>(), SkMatrix()); + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, + std::make_shared>()); auto output_bounds = SkIRect::MakeLTRB(0, 0, 100, 100); @@ -883,8 +882,8 @@ TEST(DisplayListImageFilter, RuntimeEffectGetInputDeviceBounds) { } TEST(DisplayListImageFilter, RuntimeEffectModifiesTransparentBlack) { - DlRuntimeEffectImageFilter filter_a( - nullptr, {nullptr}, std::make_shared>(), SkMatrix()); + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, + std::make_shared>()); EXPECT_FALSE(filter_a.modifies_transparent_black()); } diff --git a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc index a47c9ef008a18..49017b497240e 100644 --- a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc +++ b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc @@ -11,7 +11,6 @@ #include "flutter/display_list/effects/dl_runtime_effect.h" #include "flutter/impeller/display_list/aiks_unittests.h" -#include "include/core/SkMatrix.h" #include "include/core/SkPath.h" #include "include/core/SkRRect.h" @@ -104,34 +103,7 @@ TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { paint.setColor(DlColor::kAqua()); paint.setImageFilter(std::make_shared( DlRuntimeEffect::MakeImpeller(runtime_stage), sampler_inputs, - uniform_data, SkMatrix())); - - DisplayListBuilder builder; - builder.DrawRect(SkRect::MakeXYWH(0, 0, 400, 400), paint); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} - -TEST_P(AiksTest, CanRenderRuntimeEffectFilterWithTransform) { - auto runtime_stages = - OpenAssetAsRuntimeStage("runtime_stage_filter_example.frag.iplr"); - - auto runtime_stage = - runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())]; - ASSERT_TRUE(runtime_stage); - ASSERT_TRUE(runtime_stage->IsDirty()); - - std::vector> sampler_inputs = { - nullptr, - }; - auto uniform_data = std::make_shared>(); - uniform_data->resize(sizeof(Vector2)); - - DlPaint paint; - paint.setColor(DlColor::kAqua()); - paint.setImageFilter(std::make_shared( - DlRuntimeEffect::MakeImpeller(runtime_stage), sampler_inputs, - uniform_data, SkMatrix::Scale(0.5, 0.5))); + uniform_data)); DisplayListBuilder builder; builder.DrawRect(SkRect::MakeXYWH(0, 0, 400, 400), paint); diff --git a/impeller/display_list/image_filter.cc b/impeller/display_list/image_filter.cc index 59890abde4aa6..e1cb1e00cd905 100644 --- a/impeller/display_list/image_filter.cc +++ b/impeller/display_list/image_filter.cc @@ -137,10 +137,9 @@ std::shared_ptr WrapInput(const flutter::DlImageFilter* filter, .texture = image->image()->impeller_texture(), }); } - return FilterContents::MakeRuntimeEffect( - input, std::move(runtime_stage), uniform_data, - std::move(texture_inputs), - skia_conversions::ToMatrix(runtime_filter->matrix())); + return FilterContents::MakeRuntimeEffect(input, std::move(runtime_stage), + uniform_data, + std::move(texture_inputs)); } } FML_UNREACHABLE(); diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 92d17151b196a..ddfaa2c6388c5 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -35,28 +35,6 @@ namespace impeller { const int32_t FilterContents::kBlurFilterRequiredMipCount = GaussianBlurFilterContents::kBlurFilterRequiredMipCount; -// static -Matrix FilterContents::CalculateSubpassTransform( - const Matrix& snapshot_transform, - const Matrix& effect_transform, - const Matrix& matrix, - Entity::RenderingMode rendering_mode) { - if (rendering_mode == - Entity::RenderingMode::kSubpassAppendSnapshotTransform) { - return snapshot_transform * // - effect_transform * // - matrix * // - effect_transform.Invert(); - } else { - FML_DCHECK(rendering_mode == - Entity::RenderingMode::kSubpassPrependSnapshotTransform); - return effect_transform * // - matrix * // - effect_transform.Invert() * // - snapshot_transform; - } -} - std::shared_ptr FilterContents::MakeGaussianBlur( const FilterInput::Ref& input, Sigma sigma_x, @@ -142,14 +120,12 @@ std::shared_ptr FilterContents::MakeRuntimeEffect( FilterInput::Ref input, std::shared_ptr runtime_stage, std::shared_ptr> uniforms, - std::vector texture_inputs, - Matrix matrix) { + std::vector texture_inputs) { auto filter = std::make_shared(); filter->SetInputs({std::move(input)}); filter->SetRuntimeStage(std::move(runtime_stage)); filter->SetUniforms(std::move(uniforms)); filter->SetTextureInputs(std::move(texture_inputs)); - filter->SetMatrix(matrix); return filter; } diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index c9380673318b2..3177cf7518ee3 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -38,11 +38,6 @@ class FilterContents : public Contents { enum class MorphType { kDilate, kErode }; - static Matrix CalculateSubpassTransform(const Matrix& snapshot_transform, - const Matrix& effect_transform, - const Matrix& matrix, - Entity::RenderingMode rendering_mode); - /// Creates a gaussian blur that operates in 2 dimensions. /// See also: `MakeDirectionalGaussianBlur` static std::shared_ptr MakeGaussianBlur( @@ -88,8 +83,7 @@ class FilterContents : public Contents { FilterInput::Ref input, std::shared_ptr runtime_stage, std::shared_ptr> uniforms, - std::vector texture_inputs, - Matrix matrix = Matrix()); + std::vector texture_inputs); FilterContents(); diff --git a/impeller/entity/contents/filters/matrix_filter_contents.cc b/impeller/entity/contents/filters/matrix_filter_contents.cc index 7bee41f7b56c7..289e67fc922cb 100644 --- a/impeller/entity/contents/filters/matrix_filter_contents.cc +++ b/impeller/entity/contents/filters/matrix_filter_contents.cc @@ -24,6 +24,28 @@ void MatrixFilterContents::SetSamplerDescriptor(SamplerDescriptor desc) { sampler_descriptor_ = std::move(desc); } +namespace { +Matrix CalculateSubpassTransform(const Matrix& snapshot_transform, + const Matrix& effect_transform, + const Matrix& matrix, + Entity::RenderingMode rendering_mode) { + if (rendering_mode == + Entity::RenderingMode::kSubpassAppendSnapshotTransform) { + return snapshot_transform * // + effect_transform * // + matrix * // + effect_transform.Invert(); + } else { + FML_DCHECK(rendering_mode == + Entity::RenderingMode::kSubpassPrependSnapshotTransform); + return effect_transform * // + matrix * // + effect_transform.Invert() * // + snapshot_transform; + } +} +} // namespace + std::optional MatrixFilterContents::RenderFilter( const FilterInput::Vector& inputs, const ContentContext& renderer, diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc index afbeb54d7cc43..4bc87c443efd9 100644 --- a/impeller/entity/contents/filters/runtime_effect_filter_contents.cc +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.cc @@ -29,10 +29,6 @@ void RuntimeEffectFilterContents::SetTextureInputs( texture_inputs_ = std::move(texture_inputs); } -void RuntimeEffectFilterContents::SetMatrix(const Matrix& matrix) { - matrix_ = matrix; -} - // |FilterContents| std::optional RuntimeEffectFilterContents::RenderFilter( const FilterInput::Vector& inputs, @@ -45,26 +41,12 @@ std::optional RuntimeEffectFilterContents::RenderFilter( return std::nullopt; } - auto snapshot = inputs[0]->GetSnapshot("Matrix", renderer, entity); - if (!snapshot.has_value()) { + auto input_snapshot = + inputs[0]->GetSnapshot("RuntimeEffectContents", renderer, entity); + if (!input_snapshot.has_value()) { return std::nullopt; } - - if (rendering_mode_ == - Entity::RenderingMode::kSubpassPrependSnapshotTransform || - rendering_mode_ == - Entity::RenderingMode::kSubpassAppendSnapshotTransform) { - // See Note in MatrixFilterContents::RenderFilter - snapshot->transform = CalculateSubpassTransform( - snapshot->transform, effect_transform, matrix_, rendering_mode_); - } else { - snapshot->transform = entity.GetTransform() * // - matrix_ * // - entity.GetTransform().Invert() * // - snapshot->transform; - } - - std::optional maybe_input_coverage = snapshot->GetCoverage(); + std::optional maybe_input_coverage = input_snapshot->GetCoverage(); if (!maybe_input_coverage.has_value()) { return std::nullopt; } @@ -83,17 +65,16 @@ std::optional RuntimeEffectFilterContents::RenderFilter( // Update uniform values. std::vector texture_input_copy = texture_inputs_; - texture_input_copy[0].texture = snapshot->texture; + texture_input_copy[0].texture = input_snapshot->texture; - Size size = Size(snapshot->texture->GetSize()); - FML_LOG(ERROR) << "Size: " << size; + Size size = Size(input_snapshot->texture->GetSize()); memcpy(uniforms_->data(), &size, sizeof(Size)); //---------------------------------------------------------------------------- /// Create AnonymousContents for rendering. /// RenderProc render_proc = - [snapshot, runtime_stage = runtime_stage_, uniforms = uniforms_, + [input_snapshot, runtime_stage = runtime_stage_, uniforms = uniforms_, texture_inputs = texture_input_copy, input_coverage](const ContentContext& renderer, const Entity& entity, RenderPass& pass) -> bool { @@ -116,57 +97,15 @@ std::optional RuntimeEffectFilterContents::RenderFilter( Entity sub_entity; sub_entity.SetContents(std::move(contents)); sub_entity.SetBlendMode(entity.GetBlendMode()); - sub_entity.SetTransform(snapshot->transform); + sub_entity.SetTransform(input_snapshot->transform); return sub_entity; } -void RuntimeEffectFilterContents::SetRenderingMode( - Entity::RenderingMode rendering_mode) { - rendering_mode_ = rendering_mode; -} - -std::optional RuntimeEffectFilterContents::GetFilterCoverage( - const FilterInput::Vector& inputs, - const Entity& entity, - const Matrix& effect_transform) const { - if (inputs.empty()) { - return std::nullopt; - } - - std::optional coverage = inputs[0]->GetCoverage(entity); - if (!coverage.has_value()) { - return std::nullopt; - } - - Matrix input_transform = inputs[0]->GetTransform(entity); - if (rendering_mode_ == - Entity::RenderingMode::kSubpassPrependSnapshotTransform || - rendering_mode_ == - Entity::RenderingMode::kSubpassAppendSnapshotTransform) { - Rect coverage_bounds = coverage->TransformBounds(input_transform.Invert()); - Matrix transform = CalculateSubpassTransform( - input_transform, effect_transform, matrix_, rendering_mode_); - return coverage_bounds.TransformBounds(transform); - } else { - Matrix transform = input_transform * // - matrix_ * // - input_transform.Invert(); // - return coverage->TransformBounds(transform); - } -} - // |FilterContents| std::optional RuntimeEffectFilterContents::GetFilterSourceCoverage( const Matrix& effect_transform, const Rect& output_limit) const { - auto transform = effect_transform * // - matrix_ * // - effect_transform.Invert(); // - if (transform.GetDeterminant() == 0.0) { - return std::nullopt; - } - auto inverse = transform.Invert(); - return output_limit.TransformBounds(inverse); + return output_limit; } } // namespace impeller diff --git a/impeller/entity/contents/filters/runtime_effect_filter_contents.h b/impeller/entity/contents/filters/runtime_effect_filter_contents.h index aa72463e7fd76..3a68ecd67ab15 100644 --- a/impeller/entity/contents/filters/runtime_effect_filter_contents.h +++ b/impeller/entity/contents/filters/runtime_effect_filter_contents.h @@ -23,23 +23,10 @@ class RuntimeEffectFilterContents final : public FilterContents { void SetTextureInputs( std::vector texture_inputs); - void SetMatrix(const Matrix& matrix); - - // |FilterContents| - void SetRenderingMode(Entity::RenderingMode rendering_mode) override; - - // |FilterContents| - std::optional GetFilterCoverage( - const FilterInput::Vector& inputs, - const Entity& entity, - const Matrix& effect_transform) const override; - private: std::shared_ptr runtime_stage_; std::shared_ptr> uniforms_; std::vector texture_inputs_; - Matrix matrix_; - Entity::RenderingMode rendering_mode_ = Entity::RenderingMode::kDirect; // |FilterContents| std::optional RenderFilter( diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index 5dfb7f09447f8..f2ddb86dce296 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -120,8 +120,7 @@ void ImageFilter::initComposeFilter(ImageFilter* outer, ImageFilter* inner) { inner->filter(DlTileMode::kClamp)); } -void ImageFilter::initShader(ReusableFragmentShader* shader, - const tonic::Float64List& matrix4) { +void ImageFilter::initShader(ReusableFragmentShader* shader, const tonic::Float64List& matrix4) { FML_DCHECK(shader); filter_ = shader->as_image_filter(ToSkMatrix(matrix4)); } diff --git a/lib/ui/painting/image_filter.h b/lib/ui/painting/image_filter.h index 84a50a6c8ce9c..14ab226c918da 100644 --- a/lib/ui/painting/image_filter.h +++ b/lib/ui/painting/image_filter.h @@ -35,8 +35,7 @@ class ImageFilter : public RefCountedDartWrappable { void initMatrix(const tonic::Float64List& matrix4, int filter_quality_index); void initColorFilter(ColorFilter* colorFilter); void initComposeFilter(ImageFilter* outer, ImageFilter* inner); - void initShader(ReusableFragmentShader* shader, - const tonic::Float64List& matrix4); + void initShader(ReusableFragmentShader* shader, const tonic::Float64List& matrix4); const std::shared_ptr filter(DlTileMode mode) const; From 41f333c3cea2e9b672f0b3e3bf18964ddd373751 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 15:03:50 -0800 Subject: [PATCH 32/38] Revert "start adding matrix support." This reverts commit bc0a43602ae5ee34c7e5341fb9780eb1080b7984. --- display_list/effects/dl_image_filter.h | 49 +++++--------------------- lib/ui/painting.dart | 23 +++--------- lib/ui/painting/fragment_program.cc | 5 ++- lib/ui/painting/fragment_program.h | 4 +-- lib/ui/painting/fragment_shader.cc | 6 ++-- lib/ui/painting/fragment_shader.h | 2 +- lib/ui/painting/image_filter.cc | 4 +-- lib/ui/painting/image_filter.h | 2 +- 8 files changed, 23 insertions(+), 72 deletions(-) diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h index 18f8864ef5104..0aa85d0f6b3bc 100644 --- a/display_list/effects/dl_image_filter.h +++ b/display_list/effects/dl_image_filter.h @@ -15,7 +15,6 @@ #include "flutter/display_list/utils/dl_comparable.h" #include "flutter/fml/logging.h" -#include "include/core/SkM44.h" #include "third_party/skia/include/core/SkMatrix.h" namespace flutter { @@ -757,26 +756,23 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { explicit DlRuntimeEffectImageFilter( sk_sp runtime_effect, std::vector> samplers, - std::shared_ptr> uniform_data, - const SkMatrix& matrix) + std::shared_ptr> uniform_data) : runtime_effect_(std::move(runtime_effect)), samplers_(std::move(samplers)), - uniform_data_(std::move(uniform_data)), - matrix_(matrix) {} + uniform_data_(std::move(uniform_data)) {} std::shared_ptr shared() const override { return std::make_shared( - this->runtime_effect_, this->samplers_, this->uniform_data_, this->matrix_); + this->runtime_effect_, this->samplers_, this->uniform_data_); } static std::shared_ptr Make( sk_sp runtime_effect, std::vector> samplers, - std::shared_ptr> uniform_data, - const SkMatrix& matrix) { + std::shared_ptr> uniform_data) { return std::make_shared( std::move(runtime_effect), std::move(samplers), - std::move(uniform_data), matrix); + std::move(uniform_data)); } DlImageFilterType type() const override { @@ -786,42 +782,23 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { bool modifies_transparent_black() const override { return false; } - SkRect* map_local_bounds(const SkRect& input_bounds, + SkRect* map_local_bounds(const SkRect& input_bounds, SkRect& output_bounds) const override { - output_bounds = matrix_.mapRect(input_bounds); + output_bounds = input_bounds; return &output_bounds; } SkIRect* map_device_bounds(const SkIRect& input_bounds, const SkMatrix& ctm, SkIRect& output_bounds) const override { - SkMatrix matrix; - if (!ctm.invert(&matrix)) { - output_bounds = input_bounds; - return nullptr; - } - matrix.postConcat(matrix_); - matrix.postConcat(ctm); - SkRect device_rect; - matrix.mapRect(&device_rect, SkRect::Make(input_bounds)); - output_bounds = device_rect.roundOut(); + output_bounds = input_bounds; return &output_bounds; } SkIRect* get_input_device_bounds(const SkIRect& output_bounds, const SkMatrix& ctm, SkIRect& input_bounds) const override { - SkMatrix matrix = SkMatrix::Concat(ctm, matrix_); - SkMatrix inverse; - if (!matrix.invert(&inverse)) { - input_bounds = output_bounds; - return nullptr; - } - inverse.postConcat(ctm); - SkRect bounds; - bounds.set(output_bounds); - inverse.mapRect(&bounds); - input_bounds = bounds.roundOut(); + input_bounds = output_bounds; return &input_bounds; } @@ -841,10 +818,6 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { return uniform_data_; } - const SkMatrix& matrix() const { - return matrix_; - } - protected: bool equals_(const DlImageFilter& other) const override { FML_DCHECK(other.type() == DlImageFilterType::kRuntimeEffect); @@ -864,9 +837,6 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { return false; } } - if (matrix_ != that->matrix_) { - return false; - } return true; } @@ -874,7 +844,6 @@ class DlRuntimeEffectImageFilter final : public DlImageFilter { sk_sp runtime_effect_; std::vector> samplers_; std::shared_ptr> uniform_data_; - SkMatrix matrix_; }; } // namespace flutter diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 11a22f06311d1..e2d2680aa7279 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -4095,7 +4095,7 @@ abstract class ImageFilter { /// This API is only supported when using the Impeller rendering engine. On /// other backends a [UnsupportedError] will be thrown. To check at runtime /// whether this API is suppored use [isShaderFilterSupported]. - factory ImageFilter.shader(FragmentShader shader, { Float64List? matrix4 }) { + factory ImageFilter.shader(FragmentShader shader) { if (!_impellerEnabled) { throw UnsupportedError('ImageFilter.shader only supported with Impeller rendering engine.'); } @@ -4104,18 +4104,7 @@ abstract class ImageFilter { 'ImageFilter.shader requires that the first uniform is a ' 'vec2 and at least one sampler uniform is present.'); } - if (matrix4 != null && matrix4.length != 16) { - throw ArgumentError.value(matrix4, 'matrix4', 'matrix4" must have 16 entries.'); - } - if (matrix4 == null) { - final Float64List identity = Float64List(16); - identity[0] = 1.0; - identity[5] = 1.0; - identity[10] = 1.0; - identity[15] = 1.0; - matrix4 = identity; - } - return _FragmentShaderImageFilter(shader, matrix4); + return _FragmentShaderImageFilter(shader); } /// Whether [ImageFilter.shader] is supported on the current backend. @@ -4296,10 +4285,9 @@ class _ComposeImageFilter implements ImageFilter { } class _FragmentShaderImageFilter implements ImageFilter { - _FragmentShaderImageFilter(this.shader, this.matrix4); + _FragmentShaderImageFilter(this.shader); final FragmentShader shader; - final Float64List matrix4; late final _ImageFilter nativeFilter = _ImageFilter.shader(this); @@ -4310,7 +4298,7 @@ class _FragmentShaderImageFilter implements ImageFilter { String get _shortDescription => 'shader'; @override - String toString() => 'ImageFilter.shader(Shader#${shader.hashCode}, $matrix4)'; + String toString() => 'ImageFilter.shader(Shader#${shader.hashCode})'; @override bool operator ==(Object other) { @@ -4318,8 +4306,7 @@ class _FragmentShaderImageFilter implements ImageFilter { return false; } return other is _FragmentShaderImageFilter - && other.shader == shader - && _listEquals(other.matrix4, matrix4); + && other.shader == shader; } @override diff --git a/lib/ui/painting/fragment_program.cc b/lib/ui/painting/fragment_program.cc index 2a88bf01e5ce8..2bb8b0e7a9cfb 100644 --- a/lib/ui/painting/fragment_program.cc +++ b/lib/ui/painting/fragment_program.cc @@ -147,10 +147,9 @@ std::shared_ptr FragmentProgram::MakeDlColorSource( std::shared_ptr FragmentProgram::MakeDlImageFilter( std::shared_ptr> float_uniforms, - const std::vector>& children, - const SkMatrix& matrix) { + const std::vector>& children) { return DlRuntimeEffectImageFilter::Make(runtime_effect_, children, - std::move(float_uniforms), matrix); + std::move(float_uniforms)); } void FragmentProgram::Create(Dart_Handle wrapper) { diff --git a/lib/ui/painting/fragment_program.h b/lib/ui/painting/fragment_program.h index 1703edae3fe5a..65eb460658c51 100644 --- a/lib/ui/painting/fragment_program.h +++ b/lib/ui/painting/fragment_program.h @@ -10,7 +10,6 @@ #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/shader.h" -#include "include/core/SkMatrix.h" #include "third_party/tonic/dart_library_natives.h" #include "third_party/tonic/typed_data/typed_list.h" @@ -42,8 +41,7 @@ class FragmentProgram : public RefCountedDartWrappable { std::shared_ptr MakeDlImageFilter( std::shared_ptr> float_uniforms, - const std::vector>& children, - const SkMatrix& matrix); + const std::vector>& children); private: FragmentProgram(); diff --git a/lib/ui/painting/fragment_shader.cc b/lib/ui/painting/fragment_shader.cc index aa85d049186b7..ecf67cafe7be7 100644 --- a/lib/ui/painting/fragment_shader.cc +++ b/lib/ui/painting/fragment_shader.cc @@ -86,8 +86,7 @@ void ReusableFragmentShader::SetImageSampler(Dart_Handle index_handle, uniform_floats[float_count_ + 2 * index + 1] = image->height(); } -std::shared_ptr ReusableFragmentShader::as_image_filter( - const SkMatrix& matrix) const { +std::shared_ptr ReusableFragmentShader::as_image_filter() const { FML_CHECK(program_); // The lifetime of this object is longer than a frame, and the uniforms can be @@ -97,8 +96,7 @@ std::shared_ptr ReusableFragmentShader::as_image_filter( uniform_data->resize(uniform_data_->size()); memcpy(uniform_data->data(), uniform_data_->bytes(), uniform_data->size()); - return program_->MakeDlImageFilter(std::move(uniform_data), samplers_, - matrix); + return program_->MakeDlImageFilter(std::move(uniform_data), samplers_); } std::shared_ptr ReusableFragmentShader::shader( diff --git a/lib/ui/painting/fragment_shader.h b/lib/ui/painting/fragment_shader.h index 0480119a0b4ba..e82a22418156a 100644 --- a/lib/ui/painting/fragment_shader.h +++ b/lib/ui/painting/fragment_shader.h @@ -45,7 +45,7 @@ class ReusableFragmentShader : public Shader { // |Shader| std::shared_ptr shader(DlImageSampling) override; - std::shared_ptr as_image_filter(const SkMatrix& matrix) const; + std::shared_ptr as_image_filter() const; private: ReusableFragmentShader(fml::RefPtr program, diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index f2ddb86dce296..edc2d522f67d7 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -120,9 +120,9 @@ void ImageFilter::initComposeFilter(ImageFilter* outer, ImageFilter* inner) { inner->filter(DlTileMode::kClamp)); } -void ImageFilter::initShader(ReusableFragmentShader* shader, const tonic::Float64List& matrix4) { +void ImageFilter::initShader(ReusableFragmentShader* shader) { FML_DCHECK(shader); - filter_ = shader->as_image_filter(ToSkMatrix(matrix4)); + filter_ = shader->as_image_filter(); } } // namespace flutter diff --git a/lib/ui/painting/image_filter.h b/lib/ui/painting/image_filter.h index 14ab226c918da..642d34950bca7 100644 --- a/lib/ui/painting/image_filter.h +++ b/lib/ui/painting/image_filter.h @@ -35,7 +35,7 @@ class ImageFilter : public RefCountedDartWrappable { void initMatrix(const tonic::Float64List& matrix4, int filter_quality_index); void initColorFilter(ColorFilter* colorFilter); void initComposeFilter(ImageFilter* outer, ImageFilter* inner); - void initShader(ReusableFragmentShader* shader, const tonic::Float64List& matrix4); + void initShader(ReusableFragmentShader* shader); const std::shared_ptr filter(DlTileMode mode) const; From a87641798a239c8bc8d5c5ad7ca91611a7afe415 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 15:04:59 -0800 Subject: [PATCH 33/38] partial feedback --- display_list/skia/dl_sk_conversions.cc | 1 - .../aiks_dl_runtime_effect_unittests.cc | 2 +- impeller/display_list/image_filter.cc | 17 ++++++++--------- .../entity/contents/runtime_effect_contents.cc | 1 + .../fixtures/runtime_stage_filter_example.frag | 3 ++- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/display_list/skia/dl_sk_conversions.cc b/display_list/skia/dl_sk_conversions.cc index cd4a9e4dc9f78..de0db51cb73ae 100644 --- a/display_list/skia/dl_sk_conversions.cc +++ b/display_list/skia/dl_sk_conversions.cc @@ -234,7 +234,6 @@ sk_sp ToSk(const DlImageFilter* filter) { case DlImageFilterType::kRuntimeEffect: // UNSUPPORTED. return nullptr; - break; } } diff --git a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc index 49017b497240e..705ddc50f2699 100644 --- a/impeller/display_list/aiks_dl_runtime_effect_unittests.cc +++ b/impeller/display_list/aiks_dl_runtime_effect_unittests.cc @@ -88,7 +88,7 @@ TEST_P(AiksTest, CanRenderRuntimeEffectFilter) { auto runtime_stages = OpenAssetAsRuntimeStage("runtime_stage_filter_example.frag.iplr"); - auto runtime_stage = + std::shared_ptr runtime_stage = runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())]; ASSERT_TRUE(runtime_stage); ASSERT_TRUE(runtime_stage->IsDirty()); diff --git a/impeller/display_list/image_filter.cc b/impeller/display_list/image_filter.cc index e1cb1e00cd905..50d2489df771b 100644 --- a/impeller/display_list/image_filter.cc +++ b/impeller/display_list/image_filter.cc @@ -104,17 +104,16 @@ std::shared_ptr WrapInput(const flutter::DlImageFilter* filter, FilterInput::Make(WrapInput(inner_dl_filter.get(), input))); } case flutter::DlImageFilterType::kRuntimeEffect: { - auto fragment_program_filter = filter->asRuntimeEffectFilter(); - FML_DCHECK(fragment_program_filter); const flutter::DlRuntimeEffectImageFilter* runtime_filter = - fragment_program_filter->asRuntimeEffectFilter(); - auto runtime_stage = runtime_filter->runtime_effect()->runtime_stage(); - const auto& uniform_data = runtime_filter->uniform_data(); - const auto& samplers = runtime_filter->samplers(); + filter->asRuntimeEffectFilter(); + FML_DCHECK(runtime_filter); + std::shared_ptr runtime_stage = + runtime_filter->runtime_effect()->runtime_stage(); std::vector texture_inputs; size_t index = 0; - for (auto& sampler : samplers) { + for (const std::shared_ptr& sampler : + runtime_filter->samplers()) { if (index == 0 && sampler == nullptr) { // Insert placeholder for filter. texture_inputs.push_back( @@ -126,7 +125,7 @@ std::shared_ptr WrapInput(const flutter::DlImageFilter* filter, return nullptr; } auto* image = sampler->asImage(); - if (!sampler->asImage()) { + if (!image) { return nullptr; } FML_DCHECK(image->image()->impeller_texture()); @@ -138,7 +137,7 @@ std::shared_ptr WrapInput(const flutter::DlImageFilter* filter, }); } return FilterContents::MakeRuntimeEffect(input, std::move(runtime_stage), - uniform_data, + runtime_filter->uniform_data(), std::move(texture_inputs)); } } diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc index 7a842e8bcfd48..e099df6f0530e 100644 --- a/impeller/entity/contents/runtime_effect_contents.cc +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -249,6 +249,7 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, Context::BackendType::kVulkan); ShaderUniformSlot uniform_slot; uniform_slot.binding = uniform.location; + uniform_slot.name = uniform.name.c_str(); // TODO(jonahwilliams): rewrite this to emplace directly into // HostBuffer. diff --git a/impeller/fixtures/runtime_stage_filter_example.frag b/impeller/fixtures/runtime_stage_filter_example.frag index c7bcea0ba45d1..c26234a9185f9 100644 --- a/impeller/fixtures/runtime_stage_filter_example.frag +++ b/impeller/fixtures/runtime_stage_filter_example.frag @@ -10,5 +10,6 @@ uniform sampler2D u_texture; out vec4 frag_color; void main() { - frag_color = texture(u_texture, FlutterFragCoord().xy / u_size); + frag_color = texture(u_texture, FlutterFragCoord().xy / u_size) * + (sin(FlutterFragCoord().y) * cos(FlutterFragCoord().x)); } From 117ed3e88a2a2e4cf89c9c6592d6fcdf4386e321 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 15:11:31 -0800 Subject: [PATCH 34/38] jazz up the shader. --- impeller/fixtures/runtime_stage_filter_example.frag | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/impeller/fixtures/runtime_stage_filter_example.frag b/impeller/fixtures/runtime_stage_filter_example.frag index c26234a9185f9..83b8a4f2145bc 100644 --- a/impeller/fixtures/runtime_stage_filter_example.frag +++ b/impeller/fixtures/runtime_stage_filter_example.frag @@ -11,5 +11,6 @@ out vec4 frag_color; void main() { frag_color = texture(u_texture, FlutterFragCoord().xy / u_size) * - (sin(FlutterFragCoord().y) * cos(FlutterFragCoord().x)); + (sin(FlutterFragCoord().y / u_size.y * 3.14) * + cos(FlutterFragCoord().x / u_size.x * 3.14)); } From f16d0c627c02526dd6a680aa82f3ffa41cfa6c10 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 15:22:32 -0800 Subject: [PATCH 35/38] moar tests --- .../effects/dl_image_filter_unittests.cc | 20 +++++++++++++++ .../fixtures/shaders/general_shaders/BUILD.gn | 2 ++ .../shaders/general_shaders/missing_size.frag | 13 ++++++++++ .../general_shaders/missing_texture.frag | 13 ++++++++++ testing/dart/fragment_shader_test.dart | 25 ++++++++++++------- 5 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 lib/ui/fixtures/shaders/general_shaders/missing_size.frag create mode 100644 lib/ui/fixtures/shaders/general_shaders/missing_texture.frag diff --git a/display_list/effects/dl_image_filter_unittests.cc b/display_list/effects/dl_image_filter_unittests.cc index 842374b9d332c..8d7f0c9540b64 100644 --- a/display_list/effects/dl_image_filter_unittests.cc +++ b/display_list/effects/dl_image_filter_unittests.cc @@ -12,6 +12,7 @@ #include "flutter/display_list/utils/dl_comparable.h" #include "gtest/gtest.h" +#include "impeller/display_list/dl_image_impeller.h" #include "include/core/SkMatrix.h" #include "include/core/SkRect.h" #include "third_party/skia/include/core/SkBlendMode.h" @@ -839,6 +840,25 @@ TEST(DisplayListImageFilter, RuntimeEffectEquality) { EXPECT_NE(filter_a, filter_c); } +TEST(DisplayListImageFilter, RuntimeEffectEqualityWithSamplers) { + auto image_a = std::make_shared( + nullptr, DlTileMode::kClamp, DlTileMode::kDecal); + auto image_b = std::make_shared( + nullptr, DlTileMode::kClamp, DlTileMode::kClamp); + + DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr, image_a}, + std::make_shared>()); + DlRuntimeEffectImageFilter filter_b(nullptr, {nullptr, image_a}, + std::make_shared>()); + + EXPECT_EQ(filter_a, filter_b); + + DlRuntimeEffectImageFilter filter_c(nullptr, {nullptr, image_b}, + std::make_shared>()); + + EXPECT_NE(filter_a, filter_c); +} + TEST(DisplayListImageFilter, RuntimeEffectMapDeviceBounds) { DlRuntimeEffectImageFilter filter_a(nullptr, {nullptr}, std::make_shared>()); diff --git a/lib/ui/fixtures/shaders/general_shaders/BUILD.gn b/lib/ui/fixtures/shaders/general_shaders/BUILD.gn index 47b7a92dd4276..d14c68b0abdf9 100644 --- a/lib/ui/fixtures/shaders/general_shaders/BUILD.gn +++ b/lib/ui/fixtures/shaders/general_shaders/BUILD.gn @@ -18,6 +18,8 @@ if (enable_unittests) { "uniforms_sorted.frag", "uniform_arrays.frag", "filter_shader.frag", + "missing_size.frag", + "missing_texture.frag", ] group("general_shaders") { diff --git a/lib/ui/fixtures/shaders/general_shaders/missing_size.frag b/lib/ui/fixtures/shaders/general_shaders/missing_size.frag new file mode 100644 index 0000000000000..e6cc02dd1f750 --- /dev/null +++ b/lib/ui/fixtures/shaders/general_shaders/missing_size.frag @@ -0,0 +1,13 @@ +// 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 + +uniform sampler2D u_texture; + +out vec4 frag_color; + +void main() { + frag_color = texture(u_texture, FlutterFragCoord().xy / vec2(100)).bgra; +} diff --git a/lib/ui/fixtures/shaders/general_shaders/missing_texture.frag b/lib/ui/fixtures/shaders/general_shaders/missing_texture.frag new file mode 100644 index 0000000000000..20f80ae397397 --- /dev/null +++ b/lib/ui/fixtures/shaders/general_shaders/missing_texture.frag @@ -0,0 +1,13 @@ +// 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 + +uniform vec2 u_size; + +out vec4 frag_color; + +void main() { + frag_color = vec4(u_size.x, u_size.y, 0, 1); +} diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index 844a62e2ce004..2facf01ceed9c 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -350,18 +350,25 @@ void main() async { print('Skipped for Skia'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( + const List shaders = [ 'no_uniforms.frag.iplr', - ); - final FragmentShader shader = program.fragmentShader(); + 'missing_size.frag.iplr', + 'missing_texture.frag.iplr' + ]; + for (String fileName in shaders) { + final FragmentProgram program = await FragmentProgram.fromAsset( + fileName + ); + final FragmentShader shader = program.fragmentShader(); - Object? error; - try { - ImageFilter.shader(shader); - } catch (err) { - error = err; + Object? error; + try { + ImageFilter.shader(shader); + } catch (err) { + error = err; + } + expect(error is StateError, true); } - expect(error is StateError, true); }); test('ImageFilter.shader can be applied to canvas operations', () async { From 9dd17afaf2c20e25e913036ee586ab16fb85f63e Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 15:26:23 -0800 Subject: [PATCH 36/38] straggler. --- lib/ui/painting.dart | 9 ++++++--- testing/dart/fragment_shader_test.dart | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index e2d2680aa7279..c67d3649e85cc 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -4099,10 +4099,13 @@ abstract class ImageFilter { if (!_impellerEnabled) { throw UnsupportedError('ImageFilter.shader only supported with Impeller rendering engine.'); } - if (shader._floats.length < 2 || !shader._validateImageFilter()) { + if (shader._floats.length < 2) { throw StateError( - 'ImageFilter.shader requires that the first uniform is a ' - 'vec2 and at least one sampler uniform is present.'); + 'ImageFilter.shader requires that the first uniform is a vec2.'); + } + if (!shader._validateImageFilter()) { + throw StateError( + 'ImageFilter.shader requires that at least one sampler uniform is present.'); } return _FragmentShaderImageFilter(shader); } diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index 2facf01ceed9c..0a706a0dc1e4a 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -355,7 +355,7 @@ void main() async { 'missing_size.frag.iplr', 'missing_texture.frag.iplr' ]; - for (String fileName in shaders) { + for (final String fileName in shaders) { final FragmentProgram program = await FragmentProgram.fromAsset( fileName ); From c6d3248d06ccfaf8fd024652c6d5764ef022f675 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 15:32:36 -0800 Subject: [PATCH 37/38] ++ --- lib/ui/painting.dart | 19 ++++++++++++------- testing/dart/fragment_shader_test.dart | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index c67d3649e85cc..2cfec8d08af2c 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -4099,13 +4099,18 @@ abstract class ImageFilter { if (!_impellerEnabled) { throw UnsupportedError('ImageFilter.shader only supported with Impeller rendering engine.'); } - if (shader._floats.length < 2) { - throw StateError( - 'ImageFilter.shader requires that the first uniform is a vec2.'); - } - if (!shader._validateImageFilter()) { - throw StateError( - 'ImageFilter.shader requires that at least one sampler uniform is present.'); + final bool invalidFloats = shader._floats.length < 2; + final bool invalidSampler = !shader._validateImageFilter(); + if (invalidFloats || invalidSampler) { + final StringBuffer buffer = StringBuffer( + 'ImageFilter.shader requires that the first uniform is a vec2 and at ' + 'least one sampler uniform is present.\n'); + if (invalidFloats) { + buffer.write('The shader has fewer than two float uniforms.\n'); + } + if (invalidSampler) { + buffer.write('The shader is missing a sampler uniform.\n'); + } } return _FragmentShaderImageFilter(shader); } diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index 0a706a0dc1e4a..e065a2eb7d186 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -355,7 +355,13 @@ void main() async { 'missing_size.frag.iplr', 'missing_texture.frag.iplr' ]; - for (final String fileName in shaders) { + const List<(bool, bool)> errors = [ + (true, true), + (true, false), + (false, false) + ]; + for (int i = 0; i < 3; i++) { + final String fileName = shaders[i]; final FragmentProgram program = await FragmentProgram.fromAsset( fileName ); @@ -368,6 +374,13 @@ void main() async { error = err; } expect(error is StateError, true); + final (floatError, samplerError) = errors[i]; + if (floatError) { + expect(error.toString(), contains('shader has fewer than two float')); + } + if (samplerError) { + expect(error.toString(), contains('shader is missing a sampler uniform')); + } } }); From 10e9e5975eee4d23e47f8b28197587f9002d833c Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 7 Nov 2024 15:55:58 -0800 Subject: [PATCH 38/38] fix include. --- display_list/effects/dl_image_filter_unittests.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/display_list/effects/dl_image_filter_unittests.cc b/display_list/effects/dl_image_filter_unittests.cc index 8d7f0c9540b64..f01bb8987c5b1 100644 --- a/display_list/effects/dl_image_filter_unittests.cc +++ b/display_list/effects/dl_image_filter_unittests.cc @@ -12,7 +12,6 @@ #include "flutter/display_list/utils/dl_comparable.h" #include "gtest/gtest.h" -#include "impeller/display_list/dl_image_impeller.h" #include "include/core/SkMatrix.h" #include "include/core/SkRect.h" #include "third_party/skia/include/core/SkBlendMode.h"