mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Support deferred GPU images in ImageShaders (flutter/engine#35323)
This commit is contained in:
parent
d36bf1d5e3
commit
fd9fdbf6b8
@ -90,6 +90,7 @@ namespace flutter {
|
||||
V(SetPodColorSource) \
|
||||
V(SetSkColorSource) \
|
||||
V(SetImageColorSource) \
|
||||
V(SetRuntimeEffectColorSource) \
|
||||
\
|
||||
V(ClearImageFilter) \
|
||||
V(SetPodImageFilter) \
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include "flutter/display_list/display_list.h"
|
||||
#include "flutter/display_list/display_list_blend_mode.h"
|
||||
#include "flutter/display_list/display_list_color_source.h"
|
||||
#include "flutter/display_list/display_list_ops.h"
|
||||
|
||||
namespace flutter {
|
||||
@ -184,6 +185,12 @@ void DisplayListBuilder::onSetColorSource(const DlColorSource* source) {
|
||||
new (pod) DlSweepGradientColorSource(sweep);
|
||||
break;
|
||||
}
|
||||
case DlColorSourceType::kRuntimeEffect: {
|
||||
const DlRuntimeEffectColorSource* effect = source->asRuntimeEffect();
|
||||
FML_DCHECK(effect);
|
||||
Push<SetRuntimeEffectColorSourceOp>(0, 0, effect);
|
||||
break;
|
||||
}
|
||||
case DlColorSourceType::kUnknown:
|
||||
Push<SetSkColorSourceOp>(0, 0, source->skia_object());
|
||||
break;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/display_list/display_list_color_source.h"
|
||||
#include "display_list_color_source.h"
|
||||
#include "flutter/display_list/display_list_sampling_options.h"
|
||||
|
||||
namespace flutter {
|
||||
@ -128,4 +129,12 @@ std::shared_ptr<DlColorSource> DlColorSource::MakeSweep(
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
std::shared_ptr<DlRuntimeEffectColorSource> DlColorSource::MakeRuntimeEffect(
|
||||
sk_sp<SkRuntimeEffect> runtime_effect,
|
||||
std::vector<std::shared_ptr<DlColorSource>> samplers,
|
||||
sk_sp<SkData> uniform_data) {
|
||||
return std::make_shared<DlRuntimeEffectColorSource>(
|
||||
std::move(runtime_effect), std::move(samplers), std::move(uniform_data));
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "third_party/skia/include/core/SkShader.h"
|
||||
#include "third_party/skia/include/effects/SkGradientShader.h"
|
||||
#include "third_party/skia/include/effects/SkRuntimeEffect.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
@ -24,6 +25,7 @@ class DlLinearGradientColorSource;
|
||||
class DlRadialGradientColorSource;
|
||||
class DlConicalGradientColorSource;
|
||||
class DlSweepGradientColorSource;
|
||||
class DlRuntimeEffectColorSource;
|
||||
class DlUnknownColorSource;
|
||||
|
||||
// The DisplayList ColorSource class. This class implements all of the
|
||||
@ -43,6 +45,7 @@ enum class DlColorSourceType {
|
||||
kRadialGradient,
|
||||
kConicalGradient,
|
||||
kSweepGradient,
|
||||
kRuntimeEffect,
|
||||
kUnknown
|
||||
};
|
||||
|
||||
@ -104,6 +107,11 @@ class DlColorSource
|
||||
DlTileMode tile_mode,
|
||||
const SkMatrix* matrix = nullptr);
|
||||
|
||||
static std::shared_ptr<DlRuntimeEffectColorSource> MakeRuntimeEffect(
|
||||
sk_sp<SkRuntimeEffect> runtime_effect,
|
||||
std::vector<std::shared_ptr<DlColorSource>> samplers,
|
||||
sk_sp<SkData> uniform_data);
|
||||
|
||||
virtual bool is_opaque() const = 0;
|
||||
|
||||
virtual std::shared_ptr<DlColorSource> with_sampling(
|
||||
@ -143,6 +151,19 @@ class DlColorSource
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual const DlRuntimeEffectColorSource* asRuntimeEffect() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If this filter contains images, specifies the owning context for those
|
||||
// images.
|
||||
// Images with a DlImage::OwningContext::kRaster must only call skia_object
|
||||
// on the raster task runner.
|
||||
// A nullopt return means there is no image.
|
||||
virtual std::optional<DlImage::OwningContext> owning_context() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
protected:
|
||||
DlColorSource() = default;
|
||||
|
||||
@ -229,12 +250,10 @@ class DlImageColorSource final : public SkRefCnt,
|
||||
DlColorSourceType type() const override { return DlColorSourceType::kImage; }
|
||||
size_t size() const override { return sizeof(*this); }
|
||||
|
||||
bool is_opaque() const override {
|
||||
// TODO(109286): Consider implementing 'isOpaque' on 'DlImage'.
|
||||
if (!image_->skia_image()) {
|
||||
return false;
|
||||
}
|
||||
return image_->skia_image()->isOpaque();
|
||||
bool is_opaque() const override { return image_->isOpaque(); }
|
||||
|
||||
std::optional<DlImage::OwningContext> owning_context() const override {
|
||||
return image_->owning_context();
|
||||
}
|
||||
|
||||
sk_sp<const DlImage> image() const { return image_; }
|
||||
@ -636,6 +655,81 @@ class DlSweepGradientColorSource final : public DlGradientColorSourceBase {
|
||||
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlSweepGradientColorSource);
|
||||
};
|
||||
|
||||
class DlRuntimeEffectColorSource final : public DlColorSource {
|
||||
public:
|
||||
DlRuntimeEffectColorSource(
|
||||
sk_sp<SkRuntimeEffect> runtime_effect,
|
||||
std::vector<std::shared_ptr<DlColorSource>> samplers,
|
||||
sk_sp<SkData> uniform_data)
|
||||
: runtime_effect_(std::move(runtime_effect)),
|
||||
samplers_(std::move(samplers)),
|
||||
uniform_data_(std::move(uniform_data)) {}
|
||||
|
||||
const DlRuntimeEffectColorSource* asRuntimeEffect() const override {
|
||||
return this;
|
||||
}
|
||||
|
||||
std::shared_ptr<DlColorSource> shared() const override {
|
||||
return std::make_shared<DlRuntimeEffectColorSource>(
|
||||
runtime_effect_, samplers_, uniform_data_);
|
||||
}
|
||||
|
||||
DlColorSourceType type() const override {
|
||||
return DlColorSourceType::kRuntimeEffect;
|
||||
}
|
||||
size_t size() const override { return sizeof(*this); }
|
||||
|
||||
bool is_opaque() const override { return false; }
|
||||
|
||||
const sk_sp<SkRuntimeEffect> runtime_effect() const {
|
||||
return runtime_effect_;
|
||||
}
|
||||
const std::vector<std::shared_ptr<DlColorSource>> samplers() const {
|
||||
return samplers_;
|
||||
}
|
||||
const sk_sp<SkData> uniform_data() const { return uniform_data_; }
|
||||
|
||||
sk_sp<SkShader> skia_object() const override {
|
||||
if (!runtime_effect_) {
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<sk_sp<SkShader>> sk_samplers(samplers_.size());
|
||||
for (size_t i = 0; i < samplers_.size(); i++) {
|
||||
sk_samplers[i] = samplers_[i]->skia_object();
|
||||
}
|
||||
return runtime_effect_->makeShader(uniform_data_, sk_samplers.data(),
|
||||
sk_samplers.size());
|
||||
}
|
||||
|
||||
protected:
|
||||
bool equals_(DlColorSource const& other) const override {
|
||||
FML_DCHECK(other.type() == DlColorSourceType::kRuntimeEffect);
|
||||
auto that = static_cast<DlRuntimeEffectColorSource const*>(&other);
|
||||
if (runtime_effect_ != that->runtime_effect_) {
|
||||
return false;
|
||||
}
|
||||
if (uniform_data_ != that->uniform_data_) {
|
||||
return false;
|
||||
}
|
||||
if (samplers_.size() != that->samplers_.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < samplers_.size(); i++) {
|
||||
if (samplers_[i] != that->samplers_[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
sk_sp<SkRuntimeEffect> runtime_effect_;
|
||||
std::vector<std::shared_ptr<DlColorSource>> samplers_;
|
||||
sk_sp<SkData> uniform_data_;
|
||||
|
||||
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlRuntimeEffectColorSource);
|
||||
};
|
||||
|
||||
class DlUnknownColorSource final : public DlColorSource {
|
||||
public:
|
||||
DlUnknownColorSource(sk_sp<SkShader> shader) : sk_shader_(shader) {}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "flutter/display_list/display_list_image.h"
|
||||
#include "flutter/display_list/display_list_sampling_options.h"
|
||||
#include "flutter/display_list/types.h"
|
||||
#include "third_party/skia/include/core/SkString.h"
|
||||
#include "third_party/skia/include/core/SkSurface.h"
|
||||
|
||||
namespace flutter {
|
||||
@ -27,6 +28,15 @@ static sk_sp<DlImage> MakeTestImage(int w, int h, SkColor color) {
|
||||
return DlImage::Make(surface->makeImageSnapshot());
|
||||
}
|
||||
|
||||
static const sk_sp<SkRuntimeEffect> kTestRuntimeEffect1 =
|
||||
SkRuntimeEffect::MakeForShader(
|
||||
SkString("vec4 main(vec2 p) { return vec4(0); }"))
|
||||
.effect;
|
||||
static const sk_sp<SkRuntimeEffect> kTestRuntimeEffect2 =
|
||||
SkRuntimeEffect::MakeForShader(
|
||||
SkString("vec4 main(vec2 p) { return vec4(1); }"))
|
||||
.effect;
|
||||
|
||||
static const sk_sp<DlImage> kTestImage1 = MakeTestImage(10, 10, SK_ColorGREEN);
|
||||
static const sk_sp<DlImage> kTestAlphaImage1 =
|
||||
MakeTestImage(10, 10, SK_ColorTRANSPARENT);
|
||||
@ -138,6 +148,7 @@ TEST(DisplayListColorSource, FromSkiaImageShader) {
|
||||
ASSERT_EQ(source->asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source->asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source->asSweepGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, FromSkiaLinearGradient) {
|
||||
@ -177,6 +188,7 @@ TEST(DisplayListColorSource, FromSkiaRadialGradient) {
|
||||
ASSERT_EQ(source->asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source->asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source->asSweepGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, FromSkiaConicalGradient) {
|
||||
@ -197,6 +209,7 @@ TEST(DisplayListColorSource, FromSkiaConicalGradient) {
|
||||
ASSERT_EQ(source->asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source->asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source->asSweepGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, FromSkiaSweepGradient) {
|
||||
@ -217,6 +230,7 @@ TEST(DisplayListColorSource, FromSkiaSweepGradient) {
|
||||
ASSERT_EQ(source->asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source->asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source->asSweepGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, FromSkiaUnrecognizedShader) {
|
||||
@ -231,6 +245,7 @@ TEST(DisplayListColorSource, FromSkiaUnrecognizedShader) {
|
||||
ASSERT_EQ(source->asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source->asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source->asSweepGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, ColorConstructor) {
|
||||
@ -253,6 +268,7 @@ TEST(DisplayListColorSource, ColorAsColor) {
|
||||
ASSERT_EQ(source.asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source.asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source.asSweepGradient(), nullptr);
|
||||
ASSERT_EQ(source.asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, ColorContents) {
|
||||
@ -400,6 +416,7 @@ TEST(DisplayListColorSource, LinearGradientAsLinear) {
|
||||
ASSERT_EQ(source->asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source->asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source->asSweepGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, LinearGradientContents) {
|
||||
@ -518,6 +535,7 @@ TEST(DisplayListColorSource, RadialGradientAsRadial) {
|
||||
ASSERT_EQ(source->asLinearGradient(), nullptr);
|
||||
ASSERT_EQ(source->asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source->asSweepGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, RadialGradientContents) {
|
||||
@ -636,6 +654,7 @@ TEST(DisplayListColorSource, ConicalGradientAsConical) {
|
||||
ASSERT_EQ(source->asLinearGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source->asSweepGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, ConicalGradientContents) {
|
||||
@ -770,6 +789,7 @@ TEST(DisplayListColorSource, SweepGradientAsSweep) {
|
||||
ASSERT_EQ(source->asLinearGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source->asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source->asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, SweepGradientContents) {
|
||||
@ -888,6 +908,7 @@ TEST(DisplayListColorSource, UnknownAsNone) {
|
||||
ASSERT_EQ(source.asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source.asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source.asSweepGradient(), nullptr);
|
||||
ASSERT_EQ(source.asRuntimeEffect(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, UnknownContents) {
|
||||
@ -916,5 +937,33 @@ TEST(DisplayListColorSource, UnknownNotEquals) {
|
||||
TestNotEquals(source1, source2, "SkShader differs");
|
||||
}
|
||||
|
||||
TEST(DisplayListColorSource, RuntimeEffect) {
|
||||
std::shared_ptr<DlRuntimeEffectColorSource> source1 =
|
||||
DlColorSource::MakeRuntimeEffect(kTestRuntimeEffect1, {}, nullptr);
|
||||
std::shared_ptr<DlRuntimeEffectColorSource> source2 =
|
||||
DlColorSource::MakeRuntimeEffect(kTestRuntimeEffect2, {}, nullptr);
|
||||
std::shared_ptr<DlRuntimeEffectColorSource> source3 =
|
||||
DlColorSource::MakeRuntimeEffect(nullptr, {}, nullptr);
|
||||
|
||||
ASSERT_EQ(source1->type(), DlColorSourceType::kRuntimeEffect);
|
||||
ASSERT_EQ(source1->asRuntimeEffect(), source1.get());
|
||||
ASSERT_NE(source2->asRuntimeEffect(), source1.get());
|
||||
|
||||
ASSERT_EQ(source1->asImage(), nullptr);
|
||||
ASSERT_EQ(source1->asColor(), nullptr);
|
||||
ASSERT_EQ(source1->asLinearGradient(), nullptr);
|
||||
ASSERT_EQ(source1->asRadialGradient(), nullptr);
|
||||
ASSERT_EQ(source1->asConicalGradient(), nullptr);
|
||||
ASSERT_EQ(source1->asSweepGradient(), nullptr);
|
||||
|
||||
ASSERT_NE(source1->skia_object(), nullptr);
|
||||
ASSERT_EQ(source3->skia_object(), nullptr);
|
||||
|
||||
TestEquals(source1, source1);
|
||||
TestEquals(source3, source3);
|
||||
TestNotEquals(source1, source2, "SkRuntimeEffect differs");
|
||||
TestNotEquals(source2, source3, "SkRuntimeEffect differs");
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -54,6 +54,17 @@ class DlImage : public SkRefCnt {
|
||||
///
|
||||
virtual std::shared_ptr<impeller::Texture> impeller_texture() const = 0;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief If the pixel format of this image ignores alpha, this returns
|
||||
/// true. This method might conservatively return false when it
|
||||
/// cannot guarnatee an opaque image, for example when the pixel
|
||||
/// format of the image supports alpha but the image is made up of
|
||||
/// entirely opaque pixels.
|
||||
///
|
||||
/// @return True if the pixel format of this image ignores alpha.
|
||||
///
|
||||
virtual bool isOpaque() const = 0;
|
||||
|
||||
virtual bool isTextureBacked() const = 0;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@ -21,6 +21,11 @@ std::shared_ptr<impeller::Texture> DlImageSkia::impeller_texture() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// |DlImage|
|
||||
bool DlImageSkia::isOpaque() const {
|
||||
return image_ ? image_->isOpaque() : false;
|
||||
}
|
||||
|
||||
// |DlImage|
|
||||
bool DlImageSkia::isTextureBacked() const {
|
||||
return image_ ? image_->isTextureBacked() : false;
|
||||
|
||||
@ -23,6 +23,9 @@ class DlImageSkia final : public DlImage {
|
||||
// |DlImage|
|
||||
std::shared_ptr<impeller::Texture> impeller_texture() const override;
|
||||
|
||||
// |DlImage|
|
||||
bool isOpaque() const override;
|
||||
|
||||
// |DlImage|
|
||||
bool isTextureBacked() const override;
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_OPS_H_
|
||||
#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_OPS_H_
|
||||
|
||||
#include "display_list_color_source.h"
|
||||
#include "flutter/display_list/display_list.h"
|
||||
#include "flutter/display_list/display_list_blend_mode.h"
|
||||
#include "flutter/display_list/display_list_dispatcher.h"
|
||||
@ -246,6 +247,28 @@ struct SetImageColorSourceOp : DLOp {
|
||||
}
|
||||
};
|
||||
|
||||
// 56 bytes: 4 byte header, 4 byte padding, 8 for vtable, 8 * 2 for sk_sps, 24
|
||||
// for the std::vector.
|
||||
struct SetRuntimeEffectColorSourceOp : DLOp {
|
||||
static const auto kType = DisplayListOpType::kSetRuntimeEffectColorSource;
|
||||
|
||||
SetRuntimeEffectColorSourceOp(const DlRuntimeEffectColorSource* source)
|
||||
: source(source->runtime_effect(),
|
||||
source->samplers(),
|
||||
source->uniform_data()) {}
|
||||
|
||||
const DlRuntimeEffectColorSource source;
|
||||
|
||||
void dispatch(Dispatcher& dispatcher) const {
|
||||
dispatcher.setColorSource(&source);
|
||||
}
|
||||
|
||||
DisplayListCompare equals(const SetRuntimeEffectColorSourceOp* other) const {
|
||||
return (source == other->source) ? DisplayListCompare::kEqual
|
||||
: DisplayListCompare::kNotEqual;
|
||||
}
|
||||
};
|
||||
|
||||
// 4 byte header + 16 byte payload uses 24 total bytes (4 bytes unused)
|
||||
struct SetSharedImageFilterOp : DLOp {
|
||||
static const auto kType = DisplayListOpType::kSetSharedImageFilter;
|
||||
|
||||
@ -7,12 +7,12 @@
|
||||
|
||||
namespace flutter {
|
||||
|
||||
ShaderMaskLayer::ShaderMaskLayer(sk_sp<SkShader> shader,
|
||||
ShaderMaskLayer::ShaderMaskLayer(std::shared_ptr<DlColorSource> shader,
|
||||
const SkRect& mask_rect,
|
||||
SkBlendMode blend_mode)
|
||||
DlBlendMode blend_mode)
|
||||
: CacheableContainerLayer(
|
||||
RasterCacheUtil::kMinimumRendersBeforeCachingFilterLayer),
|
||||
shader_(shader),
|
||||
shader_(std::move(shader)),
|
||||
mask_rect_(mask_rect),
|
||||
blend_mode_(blend_mode) {}
|
||||
|
||||
@ -60,8 +60,10 @@ void ShaderMaskLayer::Paint(PaintContext& context) const {
|
||||
PaintChildren(context);
|
||||
|
||||
SkPaint paint;
|
||||
paint.setBlendMode(blend_mode_);
|
||||
paint.setShader(shader_);
|
||||
paint.setBlendMode(ToSk(blend_mode_));
|
||||
if (shader_) {
|
||||
paint.setShader(shader_->skia_object());
|
||||
}
|
||||
context.leaf_nodes_canvas->translate(mask_rect_.left(), mask_rect_.top());
|
||||
context.leaf_nodes_canvas->drawRect(
|
||||
SkRect::MakeWH(mask_rect_.width(), mask_rect_.height()), paint);
|
||||
|
||||
@ -5,16 +5,16 @@
|
||||
#ifndef FLUTTER_FLOW_LAYERS_SHADER_MASK_LAYER_H_
|
||||
#define FLUTTER_FLOW_LAYERS_SHADER_MASK_LAYER_H_
|
||||
|
||||
#include "flutter/display_list/display_list_color_source.h"
|
||||
#include "flutter/flow/layers/cacheable_layer.h"
|
||||
#include "third_party/skia/include/core/SkShader.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
class ShaderMaskLayer : public CacheableContainerLayer {
|
||||
public:
|
||||
ShaderMaskLayer(sk_sp<SkShader> shader,
|
||||
ShaderMaskLayer(std::shared_ptr<DlColorSource> shader,
|
||||
const SkRect& mask_rect,
|
||||
SkBlendMode blend_mode);
|
||||
DlBlendMode blend_mode);
|
||||
|
||||
void Diff(DiffContext* context, const Layer* old_layer) override;
|
||||
|
||||
@ -23,9 +23,9 @@ class ShaderMaskLayer : public CacheableContainerLayer {
|
||||
void Paint(PaintContext& context) const override;
|
||||
|
||||
private:
|
||||
sk_sp<SkShader> shader_;
|
||||
std::shared_ptr<DlColorSource> shader_;
|
||||
SkRect mask_rect_;
|
||||
SkBlendMode blend_mode_;
|
||||
DlBlendMode blend_mode_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ShaderMaskLayer);
|
||||
};
|
||||
|
||||
@ -23,7 +23,7 @@ using ShaderMaskLayerTest = LayerTest;
|
||||
#ifndef NDEBUG
|
||||
TEST_F(ShaderMaskLayerTest, PaintingEmptyLayerDies) {
|
||||
auto layer =
|
||||
std::make_shared<ShaderMaskLayer>(nullptr, kEmptyRect, SkBlendMode::kSrc);
|
||||
std::make_shared<ShaderMaskLayer>(nullptr, kEmptyRect, DlBlendMode::kSrc);
|
||||
|
||||
layer->Preroll(preroll_context(), SkMatrix());
|
||||
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
|
||||
@ -39,7 +39,7 @@ TEST_F(ShaderMaskLayerTest, PaintBeforePrerollDies) {
|
||||
const SkPath child_path = SkPath().addRect(child_bounds);
|
||||
auto mock_layer = std::make_shared<MockLayer>(child_path);
|
||||
auto layer =
|
||||
std::make_shared<ShaderMaskLayer>(nullptr, kEmptyRect, SkBlendMode::kSrc);
|
||||
std::make_shared<ShaderMaskLayer>(nullptr, kEmptyRect, DlBlendMode::kSrc);
|
||||
layer->Add(mock_layer);
|
||||
|
||||
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
|
||||
@ -57,7 +57,7 @@ TEST_F(ShaderMaskLayerTest, EmptyFilter) {
|
||||
const SkPaint child_paint = SkPaint(SkColors::kYellow);
|
||||
auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
|
||||
auto layer = std::make_shared<ShaderMaskLayer>(nullptr, layer_bounds,
|
||||
SkBlendMode::kSrc);
|
||||
DlBlendMode::kSrc);
|
||||
layer->Add(mock_layer);
|
||||
|
||||
layer->Preroll(preroll_context(), initial_transform);
|
||||
@ -98,9 +98,10 @@ TEST_F(ShaderMaskLayerTest, SimpleFilter) {
|
||||
const SkPaint child_paint = SkPaint(SkColors::kYellow);
|
||||
auto layer_filter =
|
||||
SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f);
|
||||
auto dl_filter = DlColorSource::From(layer_filter);
|
||||
auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
|
||||
auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds,
|
||||
SkBlendMode::kSrc);
|
||||
auto layer = std::make_shared<ShaderMaskLayer>(dl_filter, layer_bounds,
|
||||
DlBlendMode::kSrc);
|
||||
layer->Add(mock_layer);
|
||||
|
||||
layer->Preroll(preroll_context(), initial_transform);
|
||||
@ -142,10 +143,11 @@ TEST_F(ShaderMaskLayerTest, MultipleChildren) {
|
||||
const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
|
||||
auto layer_filter =
|
||||
SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f);
|
||||
auto dl_filter = DlColorSource::From(layer_filter);
|
||||
auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
|
||||
auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
|
||||
auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds,
|
||||
SkBlendMode::kSrc);
|
||||
auto layer = std::make_shared<ShaderMaskLayer>(dl_filter, layer_bounds,
|
||||
DlBlendMode::kSrc);
|
||||
layer->Add(mock_layer1);
|
||||
layer->Add(mock_layer2);
|
||||
|
||||
@ -197,14 +199,16 @@ TEST_F(ShaderMaskLayerTest, Nested) {
|
||||
const SkPaint child_paint2 = SkPaint(SkColors::kCyan);
|
||||
auto layer_filter1 =
|
||||
SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f);
|
||||
auto dl_filter1 = DlColorSource::From(layer_filter1);
|
||||
auto layer_filter2 =
|
||||
SkPerlinNoiseShader::MakeFractalNoise(2.0f, 2.0f, 2, 2.0f);
|
||||
auto dl_filter2 = DlColorSource::From(layer_filter2);
|
||||
auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
|
||||
auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
|
||||
auto layer1 = std::make_shared<ShaderMaskLayer>(layer_filter1, layer_bounds,
|
||||
SkBlendMode::kSrc);
|
||||
auto layer2 = std::make_shared<ShaderMaskLayer>(layer_filter2, layer_bounds,
|
||||
SkBlendMode::kSrc);
|
||||
auto layer1 = std::make_shared<ShaderMaskLayer>(dl_filter1, layer_bounds,
|
||||
DlBlendMode::kSrc);
|
||||
auto layer2 = std::make_shared<ShaderMaskLayer>(dl_filter2, layer_bounds,
|
||||
DlBlendMode::kSrc);
|
||||
layer2->Add(mock_layer2);
|
||||
layer1->Add(mock_layer1);
|
||||
layer1->Add(layer2);
|
||||
@ -269,8 +273,9 @@ TEST_F(ShaderMaskLayerTest, Readback) {
|
||||
const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f);
|
||||
auto layer_filter =
|
||||
SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f);
|
||||
auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds,
|
||||
SkBlendMode::kSrc);
|
||||
auto dl_filter = DlColorSource::From(layer_filter);
|
||||
auto layer = std::make_shared<ShaderMaskLayer>(dl_filter, layer_bounds,
|
||||
DlBlendMode::kSrc);
|
||||
|
||||
// ShaderMaskLayer does not read from surface
|
||||
preroll_context()->surface_needs_readback = false;
|
||||
@ -289,13 +294,14 @@ TEST_F(ShaderMaskLayerTest, Readback) {
|
||||
TEST_F(ShaderMaskLayerTest, LayerCached) {
|
||||
auto layer_filter =
|
||||
SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f);
|
||||
auto dl_filter = DlColorSource::From(layer_filter);
|
||||
SkPaint paint;
|
||||
const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f);
|
||||
auto initial_transform = SkMatrix::Translate(50.0, 25.5);
|
||||
const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f));
|
||||
auto mock_layer = std::make_shared<MockLayer>(child_path);
|
||||
auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds,
|
||||
SkBlendMode::kSrc);
|
||||
auto layer = std::make_shared<ShaderMaskLayer>(dl_filter, layer_bounds,
|
||||
DlBlendMode::kSrc);
|
||||
layer->Add(mock_layer);
|
||||
|
||||
SkMatrix cache_ctm = initial_transform;
|
||||
@ -344,7 +350,7 @@ TEST_F(ShaderMaskLayerTest, OpacityInheritance) {
|
||||
auto mock_layer = MockLayer::Make(child_path);
|
||||
const SkRect mask_rect = SkRect::MakeLTRB(10, 10, 20, 20);
|
||||
auto shader_mask_layer =
|
||||
std::make_shared<ShaderMaskLayer>(nullptr, mask_rect, SkBlendMode::kSrc);
|
||||
std::make_shared<ShaderMaskLayer>(nullptr, mask_rect, DlBlendMode::kSrc);
|
||||
shader_mask_layer->Add(mock_layer);
|
||||
|
||||
// ShaderMaskLayers can always support opacity despite incompatible children
|
||||
|
||||
@ -397,6 +397,7 @@ void DisplayListDispatcher::setColorSource(
|
||||
return;
|
||||
}
|
||||
case flutter::DlColorSourceType::kConicalGradient:
|
||||
case flutter::DlColorSourceType::kRuntimeEffect:
|
||||
case flutter::DlColorSourceType::kUnknown:
|
||||
UNIMPLEMENTED;
|
||||
break;
|
||||
|
||||
@ -29,6 +29,12 @@ std::shared_ptr<impeller::Texture> DlImageImpeller::impeller_texture() const {
|
||||
return texture_;
|
||||
}
|
||||
|
||||
// |DlImage|
|
||||
bool DlImageImpeller::isOpaque() const {
|
||||
// Impeller doesn't currently implement opaque alpha types.
|
||||
return false;
|
||||
}
|
||||
|
||||
// |DlImage|
|
||||
bool DlImageImpeller::isTextureBacked() const {
|
||||
// Impeller textures are always ... textures :/
|
||||
|
||||
@ -23,6 +23,9 @@ class DlImageImpeller final : public flutter::DlImage {
|
||||
// |DlImage|
|
||||
std::shared_ptr<impeller::Texture> impeller_texture() const override;
|
||||
|
||||
// |DlImage|
|
||||
bool isOpaque() const override;
|
||||
|
||||
// |DlImage|
|
||||
bool isTextureBacked() const override;
|
||||
|
||||
|
||||
@ -189,8 +189,7 @@ void SceneBuilder::pushShaderMask(Dart_Handle layer_handle,
|
||||
maskRectBottom);
|
||||
auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);
|
||||
auto layer = std::make_shared<flutter::ShaderMaskLayer>(
|
||||
shader->shader(sampling)->skia_object(), rect,
|
||||
static_cast<SkBlendMode>(blendMode));
|
||||
shader->shader(sampling), rect, static_cast<DlBlendMode>(blendMode));
|
||||
PushLayer(layer);
|
||||
EngineLayer::MakeRetained(layer_handle, layer);
|
||||
|
||||
|
||||
@ -51,6 +51,11 @@ std::shared_ptr<impeller::Texture> DlDeferredImageGPU::impeller_texture()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// |DlImage|
|
||||
bool DlDeferredImageGPU::isOpaque() const {
|
||||
return image_wrapper_ ? image_wrapper_->image_info().isOpaque() : false;
|
||||
}
|
||||
|
||||
// |DlImage|
|
||||
bool DlDeferredImageGPU::isTextureBacked() const {
|
||||
return image_wrapper_ ? image_wrapper_->isTextureBacked() : false;
|
||||
|
||||
@ -41,6 +41,9 @@ class DlDeferredImageGPU final : public DlImage {
|
||||
// |DlImage|
|
||||
std::shared_ptr<impeller::Texture> impeller_texture() const override;
|
||||
|
||||
// |DlImage|
|
||||
bool isOpaque() const override;
|
||||
|
||||
// |DlImage|
|
||||
bool isTextureBacked() const override;
|
||||
|
||||
|
||||
@ -29,6 +29,14 @@ std::shared_ptr<impeller::Texture> DlImageGPU::impeller_texture() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// |DlImage|
|
||||
bool DlImageGPU::isOpaque() const {
|
||||
if (auto image = skia_image()) {
|
||||
return image->isOpaque();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// |DlImage|
|
||||
bool DlImageGPU::isTextureBacked() const {
|
||||
if (auto image = skia_image()) {
|
||||
|
||||
@ -24,6 +24,9 @@ class DlImageGPU final : public DlImage {
|
||||
// |DlImage|
|
||||
std::shared_ptr<impeller::Texture> impeller_texture() const override;
|
||||
|
||||
// |DlImage|
|
||||
bool isOpaque() const override;
|
||||
|
||||
// |DlImage|
|
||||
bool isTextureBacked() const override;
|
||||
|
||||
|
||||
@ -112,7 +112,8 @@ fml::RefPtr<FragmentShader> FragmentProgram::shader(Dart_Handle shader,
|
||||
uniform_floats[i] = uniforms[i];
|
||||
}
|
||||
uniforms.Release();
|
||||
std::vector<sk_sp<SkShader>> sk_samplers(sampler_shaders.size());
|
||||
std::vector<std::shared_ptr<DlColorSource>> dl_samplers(
|
||||
sampler_shaders.size());
|
||||
for (size_t i = 0; i < sampler_shaders.size(); i++) {
|
||||
DlImageSampling sampling = DlImageSampling::kNearestNeighbor;
|
||||
ImageShader* image_shader = sampler_shaders[i];
|
||||
@ -122,13 +123,14 @@ fml::RefPtr<FragmentShader> FragmentProgram::shader(Dart_Handle shader,
|
||||
// contain a value to be used if the developer did not specify a preference
|
||||
// when they constructed the ImageShader, so we will use kNearest which is
|
||||
// the default filterQuality in a Paint object.
|
||||
sk_samplers[i] = image_shader->shader(sampling)->skia_object();
|
||||
dl_samplers[i] = image_shader->shader(sampling);
|
||||
uniform_floats[uniform_count + 2 * i] = image_shader->width();
|
||||
uniform_floats[uniform_count + 2 * i + 1] = image_shader->height();
|
||||
}
|
||||
auto sk_shader = runtime_effect_->makeShader(
|
||||
std::move(uniform_data), sk_samplers.data(), sk_samplers.size());
|
||||
return FragmentShader::Create(shader, std::move(sk_shader));
|
||||
return FragmentShader::Create(
|
||||
shader,
|
||||
DlColorSource::MakeRuntimeEffect(runtime_effect_, std::move(dl_samplers),
|
||||
std::move(uniform_data)));
|
||||
}
|
||||
|
||||
void FragmentProgram::Create(Dart_Handle wrapper) {
|
||||
|
||||
@ -34,15 +34,17 @@ std::shared_ptr<DlColorSource> FragmentShader::shader(
|
||||
return source_;
|
||||
}
|
||||
|
||||
fml::RefPtr<FragmentShader> FragmentShader::Create(Dart_Handle dart_handle,
|
||||
sk_sp<SkShader> shader) {
|
||||
fml::RefPtr<FragmentShader> FragmentShader::Create(
|
||||
Dart_Handle dart_handle,
|
||||
std::shared_ptr<DlRuntimeEffectColorSource> shader) {
|
||||
auto fragment_shader = fml::MakeRefCounted<FragmentShader>(std::move(shader));
|
||||
fragment_shader->AssociateWithDartWrapper(dart_handle);
|
||||
return fragment_shader;
|
||||
}
|
||||
|
||||
FragmentShader::FragmentShader(sk_sp<SkShader> shader)
|
||||
: source_(DlColorSource::From(shader)) {}
|
||||
FragmentShader::FragmentShader(
|
||||
std::shared_ptr<DlRuntimeEffectColorSource> shader)
|
||||
: source_(std::move(shader)) {}
|
||||
|
||||
FragmentShader::~FragmentShader() = default;
|
||||
|
||||
|
||||
@ -25,15 +25,16 @@ class FragmentShader : public Shader {
|
||||
|
||||
public:
|
||||
~FragmentShader() override;
|
||||
static fml::RefPtr<FragmentShader> Create(Dart_Handle dart_handle,
|
||||
sk_sp<SkShader> shader);
|
||||
static fml::RefPtr<FragmentShader> Create(
|
||||
Dart_Handle dart_handle,
|
||||
std::shared_ptr<DlRuntimeEffectColorSource> shader);
|
||||
|
||||
std::shared_ptr<DlColorSource> shader(DlImageSampling) override;
|
||||
|
||||
private:
|
||||
explicit FragmentShader(sk_sp<SkShader> shader);
|
||||
explicit FragmentShader(std::shared_ptr<DlRuntimeEffectColorSource> shader);
|
||||
|
||||
std::shared_ptr<DlColorSource> source_;
|
||||
std::shared_ptr<DlRuntimeEffectColorSource> source_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -32,12 +32,6 @@ Dart_Handle ImageShader::initWithImage(CanvasImage* image,
|
||||
return ToDart("ImageShader constructor called with non-genuine Image.");
|
||||
}
|
||||
|
||||
if (image->image()->owning_context() != DlImage::OwningContext::kIO) {
|
||||
// TODO(dnfield): it should be possible to support this
|
||||
// https://github.com/flutter/flutter/issues/105085
|
||||
return ToDart("ImageShader constructor with GPU image is not supported.");
|
||||
}
|
||||
|
||||
image_ = image->image();
|
||||
tonic::Float64List matrix4(matrix_handle);
|
||||
SkMatrix local_matrix = ToSkMatrix(matrix4);
|
||||
|
||||
@ -98,7 +98,14 @@ const SkPaint* Paint::paint(SkPaint& paint) const {
|
||||
Shader* decoded = tonic::DartConverter<Shader*>::FromDart(shader);
|
||||
auto sampling =
|
||||
ImageFilter::SamplingFromIndex(uint_data[kFilterQualityIndex]);
|
||||
paint.setShader(decoded->shader(sampling)->skia_object());
|
||||
auto color_source = decoded->shader(sampling);
|
||||
// TODO(dnfield): Remove this restriction.
|
||||
// This currently is only used by paragraph code. Once SkParagraph does
|
||||
// not need to take an SkPaint, we won't be restricted in this way because
|
||||
// we will not need to access the shader on the UI task runner.
|
||||
if (color_source->owning_context() != DlImage::OwningContext::kRaster) {
|
||||
paint.setShader(color_source->skia_object());
|
||||
}
|
||||
}
|
||||
|
||||
Dart_Handle color_filter = values[kColorFilterIndex];
|
||||
|
||||
@ -36,8 +36,25 @@ void main() async {
|
||||
samplerUniforms: <ImageShader>[imageShader],
|
||||
);
|
||||
await _expectShaderRendersGreen(shader);
|
||||
blueGreenImage.dispose();
|
||||
});
|
||||
|
||||
test('blue-green image renders green - GPU image', () async {
|
||||
final FragmentProgram program = await FragmentProgram.fromAsset(
|
||||
'blue_green_sampler.frag.iplr',
|
||||
);
|
||||
final Image blueGreenImage = _createBlueGreenImageSync();
|
||||
final ImageShader imageShader = ImageShader(
|
||||
blueGreenImage, TileMode.clamp, TileMode.clamp, _identityMatrix);
|
||||
final Shader shader = program.shader(
|
||||
floatUniforms: Float32List.fromList(<double>[]),
|
||||
samplerUniforms: <ImageShader>[imageShader],
|
||||
);
|
||||
await _expectShaderRendersGreen(shader);
|
||||
blueGreenImage.dispose();
|
||||
});
|
||||
|
||||
|
||||
test('shader with uniforms renders correctly', () async {
|
||||
final FragmentProgram program = await FragmentProgram.fromAsset(
|
||||
'uniforms.frag.iplr',
|
||||
@ -318,6 +335,21 @@ Future<Image> _createBlueGreenImage() async {
|
||||
return frame.image;
|
||||
}
|
||||
|
||||
// A 10x10 image where the left half is blue and the right half is green.
|
||||
Image _createBlueGreenImageSync() {
|
||||
final PictureRecorder recorder = PictureRecorder();
|
||||
final Canvas canvas = Canvas(recorder);
|
||||
canvas.drawRect(const Rect.fromLTWH(0, 0, 5, 10), Paint()..color = const Color(0xFF0000FF));
|
||||
canvas.drawRect(const Rect.fromLTWH(5, 0, 5, 10), Paint()..color = const Color(0xFF00FF00));
|
||||
final Picture picture = recorder.endRecording();
|
||||
try {
|
||||
return picture.toImageSync(10, 10);
|
||||
} finally {
|
||||
picture.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final Float64List _identityMatrix = Float64List.fromList(<double>[
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
|
||||
@ -25,11 +25,9 @@ void main() {
|
||||
final Image image = picture.toImageSync(50, 50);
|
||||
picture.dispose();
|
||||
|
||||
// TODO(dnfield): this should not throw once
|
||||
// https://github.com/flutter/flutter/issues/105085 is fixed.
|
||||
expect(
|
||||
() => ImageShader(image, TileMode.clamp, TileMode.clamp, Float64List(16)),
|
||||
throwsException,
|
||||
);
|
||||
final ImageShader shader = ImageShader(image, TileMode.clamp, TileMode.clamp, Float64List(16));
|
||||
final Paint paint = Paint()..shader=shader;
|
||||
const Rect rect = Rect.fromLTRB(0, 0, 100, 100);
|
||||
testCanvas((Canvas canvas) => canvas.drawRect(rect, paint));
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user