Support deferred GPU images in ImageShaders (flutter/engine#35323)

This commit is contained in:
Dan Field 2022-08-18 16:12:11 -07:00 committed by GitHub
parent d36bf1d5e3
commit fd9fdbf6b8
27 changed files with 334 additions and 60 deletions

View File

@ -90,6 +90,7 @@ namespace flutter {
V(SetPodColorSource) \
V(SetSkColorSource) \
V(SetImageColorSource) \
V(SetRuntimeEffectColorSource) \
\
V(ClearImageFilter) \
V(SetPodImageFilter) \

View File

@ -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;

View File

@ -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

View File

@ -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) {}

View File

@ -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

View File

@ -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;
//----------------------------------------------------------------------------

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);
};

View File

@ -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

View File

@ -397,6 +397,7 @@ void DisplayListDispatcher::setColorSource(
return;
}
case flutter::DlColorSourceType::kConicalGradient:
case flutter::DlColorSourceType::kRuntimeEffect:
case flutter::DlColorSourceType::kUnknown:
UNIMPLEMENTED;
break;

View File

@ -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 :/

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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()) {

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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];

View File

@ -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,

View File

@ -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));
});
}