diff --git a/examples/examples.gni b/examples/examples.gni index 6779ee16b905e..4c267169d37ef 100644 --- a/examples/examples.gni +++ b/examples/examples.gni @@ -4,6 +4,6 @@ declare_args() { # The example embedders may use dependencies not suitable on all platforms. - # Use this GN arg to disable building the examples. - build_embedder_examples = is_mac || is_linux + # Use this GN arg to enable building the examples. + build_embedder_examples = false } diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index 548db3227d72e..8e8ef2628a16b 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -25,15 +25,11 @@ void BackdropFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { context->AddLayerBounds(paint_bounds); if (filter_) { - // convert paint bounds and filter to screen coordinates context->GetTransform().mapRect(&paint_bounds); auto input_filter_bounds = paint_bounds.roundOut(); - auto filter = filter_->makeWithLocalMatrix(context->GetTransform()); - auto filter_bounds = // in screen coordinates - filter->filterBounds(input_filter_bounds, SkMatrix::I(), - SkImageFilter::kReverse_MapDirection); - + filter_->filterBounds(input_filter_bounds, context->GetTransform(), + SkImageFilter::kReverse_MapDirection); context->AddReadbackRegion(filter_bounds); } diff --git a/flow/layers/backdrop_filter_layer_unittests.cc b/flow/layers/backdrop_filter_layer_unittests.cc index 7a5a066fd7344..667b0dc713d28 100644 --- a/flow/layers/backdrop_filter_layer_unittests.cc +++ b/flow/layers/backdrop_filter_layer_unittests.cc @@ -327,5 +327,29 @@ TEST_F(BackdropLayerDiffTest, BackdropLayer) { EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 190, 190)); } +TEST_F(BackdropLayerDiffTest, BackdropLayerInvalidTransform) { + auto filter = SkImageFilters::Blur(10, 10, SkTileMode::kClamp, nullptr); + + { + // tests later assume 30px readback area, fail early if that's not the case + auto readback = filter->filterBounds(SkIRect::MakeWH(10, 10), SkMatrix::I(), + SkImageFilter::kReverse_MapDirection); + EXPECT_EQ(readback, SkIRect::MakeLTRB(-30, -30, 40, 40)); + } + + MockLayerTree l1(SkISize::Make(100, 100)); + SkMatrix transform; + transform.setPerspX(0.1); + transform.setPerspY(0.1); + + auto transform_layer = std::make_shared(transform); + l1.root()->Add(transform_layer); + transform_layer->Add( + std::make_shared(filter, SkBlendMode::kSrcOver)); + + auto damage = DiffLayerTree(l1, MockLayerTree(SkISize::Make(100, 100))); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeWH(15, 15)); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java b/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java index b9a9c57d95de4..55f183c6c6cd6 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java @@ -19,6 +19,7 @@ public class SurfaceTextureWrapper { private SurfaceTexture surfaceTexture; private boolean released; + private boolean attached; public SurfaceTextureWrapper(@NonNull SurfaceTexture surfaceTexture) { this.surfaceTexture = surfaceTexture; @@ -45,6 +46,7 @@ public void release() { if (!released) { surfaceTexture.release(); released = true; + attached = false; } } } @@ -53,16 +55,33 @@ public void release() { @SuppressWarnings("unused") public void attachToGLContext(int texName) { synchronized (this) { - if (!released) { - surfaceTexture.attachToGLContext(texName); + if (released) { + return; + } + // When the rasterizer tasks run on a different thread, the GrContext is re-created. + // This causes the texture to be in an uninitialized state. + // This should *not* be an issue once platform views are always rendered as TextureLayers + // since thread merging will be always disabled on Android. + // For more see: AndroidExternalTextureGL::OnGrContextCreated in + // android_external_texture_gl.cc, and + // https://github.com/flutter/flutter/issues/98155 + if (attached) { + surfaceTexture.detachFromGLContext(); } + surfaceTexture.attachToGLContext(texName); + attached = true; } } // Called by native. @SuppressWarnings("unused") public void detachFromGLContext() { - surfaceTexture.detachFromGLContext(); + synchronized (this) { + if (attached && !released) { + surfaceTexture.detachFromGLContext(); + attached = false; + } + } } // Called by native. diff --git a/shell/platform/android/test/io/flutter/embedding/engine/renderer/SurfaceTextureWrapperTest.java b/shell/platform/android/test/io/flutter/embedding/engine/renderer/SurfaceTextureWrapperTest.java new file mode 100644 index 0000000000000..a9395f675235a --- /dev/null +++ b/shell/platform/android/test/io/flutter/embedding/engine/renderer/SurfaceTextureWrapperTest.java @@ -0,0 +1,82 @@ +package io.flutter.embedding.engine.renderer; + +import static junit.framework.TestCase.*; +import static org.mockito.Mockito.*; + +import android.graphics.SurfaceTexture; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class SurfaceTextureWrapperTest { + + @Test + public void attachToGLContext() { + final SurfaceTexture tx = mock(SurfaceTexture.class); + final SurfaceTextureWrapper wrapper = new SurfaceTextureWrapper(tx); + + wrapper.attachToGLContext(0); + verify(tx, times(1)).attachToGLContext(0); + verifyNoMoreInteractions(tx); + } + + @Test + public void attachToGLContext_detachesFromCurrentContext() { + final SurfaceTexture tx = mock(SurfaceTexture.class); + final SurfaceTextureWrapper wrapper = new SurfaceTextureWrapper(tx); + + wrapper.attachToGLContext(0); + + reset(tx); + + wrapper.attachToGLContext(0); + verify(tx, times(1)).detachFromGLContext(); + verify(tx, times(1)).attachToGLContext(0); + verifyNoMoreInteractions(tx); + } + + @Test + public void attachToGLContext_doesNotDetacheFromCurrentContext() { + final SurfaceTexture tx = mock(SurfaceTexture.class); + final SurfaceTextureWrapper wrapper = new SurfaceTextureWrapper(tx); + + wrapper.attachToGLContext(0); + + wrapper.detachFromGLContext(); + + reset(tx); + + wrapper.attachToGLContext(0); + verify(tx, times(1)).attachToGLContext(0); + verifyNoMoreInteractions(tx); + } + + @Test + public void detachFromGLContext() { + final SurfaceTexture tx = mock(SurfaceTexture.class); + final SurfaceTextureWrapper wrapper = new SurfaceTextureWrapper(tx); + + wrapper.attachToGLContext(0); + reset(tx); + + wrapper.detachFromGLContext(); + verify(tx, times(1)).detachFromGLContext(); + verifyNoMoreInteractions(tx); + } + + @Test + public void release() { + final SurfaceTexture tx = mock(SurfaceTexture.class); + final SurfaceTextureWrapper wrapper = new SurfaceTextureWrapper(tx); + + wrapper.release(); + + verify(tx, times(1)).release(); + reset(tx); + + wrapper.detachFromGLContext(); + wrapper.attachToGLContext(0); + verifyNoMoreInteractions(tx); + } +} diff --git a/shell/platform/android/test_runner/build.gradle b/shell/platform/android/test_runner/build.gradle index bb9a1fc781375..826c1f8dba3cb 100644 --- a/shell/platform/android/test_runner/build.gradle +++ b/shell/platform/android/test_runner/build.gradle @@ -46,6 +46,7 @@ android { testImplementation "com.ibm.icu:icu4j:69.1" testImplementation "org.robolectric:robolectric:4.6.1" testImplementation "junit:junit:4.13" + testImplementation "androidx.test.ext:junit:1.1.3" def mockitoVersion = "4.1.0" testImplementation "org.mockito:mockito-core:$mockitoVersion"