| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| #include "platform/graphics/Canvas2DLayerBridge.h" |
| |
| #include "cc/resources/single_release_callback.h" |
| #include "cc/resources/texture_mailbox.h" |
| #include "cc/test/test_gpu_memory_buffer_manager.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "gpu/command_buffer/common/capabilities.h" |
| #include "platform/CrossThreadFunctional.h" |
| #include "platform/WaitableEvent.h" |
| #include "platform/WebTaskRunner.h" |
| #include "platform/graphics/ImageBuffer.h" |
| #include "platform/graphics/UnacceleratedImageBufferSurface.h" |
| #include "platform/graphics/paint/PaintFlags.h" |
| #include "platform/graphics/paint/PaintSurface.h" |
| #include "platform/graphics/test/FakeGLES2Interface.h" |
| #include "platform/graphics/test/FakeWebGraphicsContext3DProvider.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebExternalBitmap.h" |
| #include "public/platform/WebScheduler.h" |
| #include "public/platform/WebThread.h" |
| #include "public/platform/WebTraceLocation.h" |
| #include "skia/ext/texture_handle.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h" |
| #include "third_party/skia/include/gpu/gl/GrGLTypes.h" |
| #include "wtf/PtrUtil.h" |
| #include "wtf/RefPtr.h" |
| |
| #include <memory> |
| |
| using testing::AnyNumber; |
| using testing::AtLeast; |
| using testing::InSequence; |
| using testing::Return; |
| using testing::Test; |
| using testing::_; |
| |
| namespace blink { |
| |
| namespace { |
| |
| class Canvas2DLayerBridgePtr { |
| public: |
| Canvas2DLayerBridgePtr() {} |
| Canvas2DLayerBridgePtr(PassRefPtr<Canvas2DLayerBridge> layerBridge) |
| : m_layerBridge(layerBridge) {} |
| |
| ~Canvas2DLayerBridgePtr() { clear(); } |
| |
| void clear() { |
| if (m_layerBridge) { |
| m_layerBridge->beginDestruction(); |
| m_layerBridge.clear(); |
| } |
| } |
| |
| void operator=(PassRefPtr<Canvas2DLayerBridge> layerBridge) { |
| ASSERT(!m_layerBridge); |
| m_layerBridge = layerBridge; |
| } |
| |
| Canvas2DLayerBridge* operator->() { return m_layerBridge.get(); } |
| Canvas2DLayerBridge* get() { return m_layerBridge.get(); } |
| |
| private: |
| RefPtr<Canvas2DLayerBridge> m_layerBridge; |
| }; |
| |
| class FakeGLES2InterfaceWithImageSupport : public FakeGLES2Interface { |
| public: |
| GLuint CreateImageCHROMIUM(ClientBuffer, GLsizei, GLsizei, GLenum) override { |
| return ++m_createImageCount; |
| } |
| void DestroyImageCHROMIUM(GLuint) override { ++m_destroyImageCount; } |
| |
| GLuint createImageCount() { return m_createImageCount; } |
| GLuint destroyImageCount() { return m_destroyImageCount; } |
| |
| private: |
| GLuint m_createImageCount = 0; |
| GLuint m_destroyImageCount = 0; |
| }; |
| |
| class FakePlatformSupport : public TestingPlatformSupport { |
| gpu::GpuMemoryBufferManager* getGpuMemoryBufferManager() override { |
| return &m_testGpuMemoryBufferManager; |
| } |
| |
| private: |
| cc::TestGpuMemoryBufferManager m_testGpuMemoryBufferManager; |
| }; |
| |
| } // anonymous namespace |
| |
| class Canvas2DLayerBridgeTest : public Test { |
| public: |
| PassRefPtr<Canvas2DLayerBridge> makeBridge( |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> provider, |
| const IntSize& size, |
| Canvas2DLayerBridge::AccelerationMode accelerationMode) { |
| RefPtr<Canvas2DLayerBridge> bridge = adoptRef(new Canvas2DLayerBridge( |
| std::move(provider), size, 0, NonOpaque, accelerationMode, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType)); |
| bridge->dontUseIdleSchedulingForTesting(); |
| return bridge.release(); |
| } |
| |
| protected: |
| void fullLifecycleTest() { |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::DisableAcceleration, gfx::ColorSpace::CreateSRGB(), |
| false, kN32_SkColorType))); |
| |
| const GrGLTextureInfo* textureInfo = skia::GrBackendObjectToGrGLTextureInfo( |
| bridge->newImageSnapshot(PreferAcceleration, SnapshotReasonUnitTests) |
| ->getTextureHandle(true)); |
| EXPECT_EQ(textureInfo, nullptr); |
| bridge.clear(); |
| } |
| |
| void fallbackToSoftwareIfContextLost() { |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| |
| gl.setIsContextLost(true); |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::EnableAcceleration, gfx::ColorSpace::CreateSRGB(), |
| false, kN32_SkColorType))); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| } |
| |
| void fallbackToSoftwareOnFailedTextureAlloc() { |
| { |
| // No fallback case. |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::EnableAcceleration, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| EXPECT_TRUE(bridge->isAccelerated()); |
| sk_sp<SkImage> snapshot = |
| bridge->newImageSnapshot(PreferAcceleration, SnapshotReasonUnitTests); |
| EXPECT_TRUE(bridge->isAccelerated()); |
| EXPECT_TRUE(snapshot->isTextureBacked()); |
| } |
| |
| { |
| // Fallback case. |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| GrContext* gr = contextProvider->grContext(); |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::EnableAcceleration, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| EXPECT_TRUE(bridge->isAccelerated()); // We don't yet know that |
| // allocation will fail. |
| // This will cause SkSurface_Gpu creation to fail without |
| // Canvas2DLayerBridge otherwise detecting that anything was disabled. |
| gr->abandonContext(); |
| sk_sp<SkImage> snapshot = |
| bridge->newImageSnapshot(PreferAcceleration, SnapshotReasonUnitTests); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_FALSE(snapshot->isTextureBacked()); |
| } |
| } |
| |
| void noDrawOnContextLostTest() { |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::ForceAccelerationForTesting, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| PaintFlags flags; |
| uint32_t genID = bridge->getOrCreateSurface()->generationID(); |
| bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); |
| EXPECT_EQ(genID, bridge->getOrCreateSurface()->generationID()); |
| gl.setIsContextLost(true); |
| EXPECT_EQ(genID, bridge->getOrCreateSurface()->generationID()); |
| bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); |
| EXPECT_EQ(genID, bridge->getOrCreateSurface()->generationID()); |
| // This results in the internal surface being torn down in response to the |
| // context loss. |
| EXPECT_FALSE(bridge->checkSurfaceValid()); |
| EXPECT_EQ(nullptr, bridge->getOrCreateSurface()); |
| // The following passes by not crashing |
| bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); |
| bridge->flush(); |
| } |
| |
| void prepareMailboxWhenContextIsLost() { |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::ForceAccelerationForTesting, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| |
| EXPECT_TRUE(bridge->isAccelerated()); |
| |
| // When the context is lost we are not sure if we should still be producing |
| // GL frames for the compositor or not, so fail to generate frames. |
| gl.setIsContextLost(true); |
| |
| cc::TextureMailbox textureMailbox; |
| std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; |
| EXPECT_FALSE( |
| bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback)); |
| } |
| |
| void prepareMailboxWhenContextIsLostWithFailedRestore() { |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::ForceAccelerationForTesting, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| |
| bridge->getOrCreateSurface(); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| // When the context is lost we are not sure if we should still be producing |
| // GL frames for the compositor or not, so fail to generate frames. |
| gl.setIsContextLost(true); |
| EXPECT_FALSE(bridge->checkSurfaceValid()); |
| |
| // Restoration will fail because |
| // Platform::createSharedOffscreenGraphicsContext3DProvider() is stubbed |
| // in unit tests. This simulates what would happen when attempting to |
| // restore while the GPU process is down. |
| bridge->restoreSurface(); |
| |
| cc::TextureMailbox textureMailbox; |
| std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; |
| EXPECT_FALSE( |
| bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback)); |
| } |
| |
| void prepareMailboxAndLoseResourceTest() { |
| // Prepare a mailbox, then report the resource as lost. |
| // This test passes by not crashing and not triggering assertions. |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::ForceAccelerationForTesting, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| |
| cc::TextureMailbox textureMailbox; |
| std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; |
| EXPECT_TRUE( |
| bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback)); |
| |
| bool lostResource = true; |
| releaseCallback->Run(gpu::SyncToken(), lostResource); |
| } |
| |
| // Retry with mailbox released while bridge destruction is in progress. |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| |
| cc::TextureMailbox textureMailbox; |
| std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; |
| |
| { |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::ForceAccelerationForTesting, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback); |
| // |bridge| goes out of scope and would normally be destroyed, but |
| // object is kept alive by self references. |
| } |
| |
| // This should cause the bridge to be destroyed. |
| bool lostResource = true; |
| // Before fixing crbug.com/411864, the following line would cause a memory |
| // use after free that sometimes caused a crash in normal builds and |
| // crashed consistently with ASAN. |
| releaseCallback->Run(gpu::SyncToken(), lostResource); |
| } |
| } |
| |
| void accelerationHintTest() { |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 300), 0, NonOpaque, |
| Canvas2DLayerBridge::EnableAcceleration, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| PaintFlags flags; |
| bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); |
| sk_sp<SkImage> image = |
| bridge->newImageSnapshot(PreferAcceleration, SnapshotReasonUnitTests); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| EXPECT_TRUE(bridge->isAccelerated()); |
| } |
| |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 300), 0, NonOpaque, |
| Canvas2DLayerBridge::EnableAcceleration, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| PaintFlags flags; |
| bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); |
| sk_sp<SkImage> image = bridge->newImageSnapshot(PreferNoAcceleration, |
| SnapshotReasonUnitTests); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| } |
| } |
| }; |
| |
| TEST_F(Canvas2DLayerBridgeTest, FullLifecycleSingleThreaded) { |
| fullLifecycleTest(); |
| } |
| |
| TEST_F(Canvas2DLayerBridgeTest, NoDrawOnContextLost) { |
| noDrawOnContextLostTest(); |
| } |
| |
| TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxWhenContextIsLost) { |
| prepareMailboxWhenContextIsLost(); |
| } |
| |
| TEST_F(Canvas2DLayerBridgeTest, |
| PrepareMailboxWhenContextIsLostWithFailedRestore) { |
| prepareMailboxWhenContextIsLostWithFailedRestore(); |
| } |
| |
| TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxAndLoseResource) { |
| prepareMailboxAndLoseResourceTest(); |
| } |
| |
| TEST_F(Canvas2DLayerBridgeTest, AccelerationHint) { |
| accelerationHintTest(); |
| } |
| |
| TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareIfContextLost) { |
| fallbackToSoftwareIfContextLost(); |
| } |
| |
| TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareOnFailedTextureAlloc) { |
| fallbackToSoftwareOnFailedTextureAlloc(); |
| } |
| |
| class MockLogger : public Canvas2DLayerBridge::Logger { |
| public: |
| MOCK_METHOD1(reportHibernationEvent, |
| void(Canvas2DLayerBridge::HibernationEvent)); |
| MOCK_METHOD0(didStartHibernating, void()); |
| virtual ~MockLogger() {} |
| }; |
| |
| void runCreateBridgeTask(Canvas2DLayerBridgePtr* bridgePtr, |
| gpu::gles2::GLES2Interface* gl, |
| Canvas2DLayerBridgeTest* testHost, |
| WaitableEvent* doneEvent) { |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::makeUnique<FakeWebGraphicsContext3DProvider>(gl); |
| *bridgePtr = |
| testHost->makeBridge(std::move(contextProvider), IntSize(300, 300), |
| Canvas2DLayerBridge::EnableAcceleration); |
| // draw+flush to trigger the creation of a GPU surface |
| (*bridgePtr)->didDraw(FloatRect(0, 0, 1, 1)); |
| (*bridgePtr)->finalizeFrame(); |
| (*bridgePtr)->flush(); |
| doneEvent->signal(); |
| } |
| |
| void postAndWaitCreateBridgeTask(const WebTraceLocation& location, |
| WebThread* testThread, |
| Canvas2DLayerBridgePtr* bridgePtr, |
| gpu::gles2::GLES2Interface* gl, |
| Canvas2DLayerBridgeTest* testHost) { |
| std::unique_ptr<WaitableEvent> bridgeCreatedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| testThread->getWebTaskRunner()->postTask( |
| location, crossThreadBind( |
| &runCreateBridgeTask, crossThreadUnretained(bridgePtr), |
| crossThreadUnretained(gl), crossThreadUnretained(testHost), |
| crossThreadUnretained(bridgeCreatedEvent.get()))); |
| bridgeCreatedEvent->wait(); |
| } |
| |
| void runDestroyBridgeTask(Canvas2DLayerBridgePtr* bridgePtr, |
| WaitableEvent* doneEvent) { |
| bridgePtr->clear(); |
| if (doneEvent) |
| doneEvent->signal(); |
| } |
| |
| void postDestroyBridgeTask(const WebTraceLocation& location, |
| WebThread* testThread, |
| Canvas2DLayerBridgePtr* bridgePtr) { |
| testThread->getWebTaskRunner()->postTask( |
| location, crossThreadBind(&runDestroyBridgeTask, |
| crossThreadUnretained(bridgePtr), nullptr)); |
| } |
| |
| void postAndWaitDestroyBridgeTask(const WebTraceLocation& location, |
| WebThread* testThread, |
| Canvas2DLayerBridgePtr* bridgePtr) { |
| std::unique_ptr<WaitableEvent> bridgeDestroyedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| testThread->getWebTaskRunner()->postTask( |
| location, |
| crossThreadBind(&runDestroyBridgeTask, crossThreadUnretained(bridgePtr), |
| crossThreadUnretained(bridgeDestroyedEvent.get()))); |
| bridgeDestroyedEvent->wait(); |
| } |
| |
| void runSetIsHiddenTask(Canvas2DLayerBridge* bridge, |
| bool value, |
| WaitableEvent* doneEvent) { |
| bridge->setIsHidden(value); |
| if (doneEvent) |
| doneEvent->signal(); |
| } |
| |
| void postSetIsHiddenTask(const WebTraceLocation& location, |
| WebThread* testThread, |
| Canvas2DLayerBridge* bridge, |
| bool value, |
| WaitableEvent* doneEvent = nullptr) { |
| testThread->getWebTaskRunner()->postTask( |
| location, |
| crossThreadBind(&runSetIsHiddenTask, crossThreadUnretained(bridge), value, |
| crossThreadUnretained(doneEvent))); |
| } |
| |
| void postAndWaitSetIsHiddenTask(const WebTraceLocation& location, |
| WebThread* testThread, |
| Canvas2DLayerBridge* bridge, |
| bool value) { |
| std::unique_ptr<WaitableEvent> doneEvent = WTF::makeUnique<WaitableEvent>(); |
| postSetIsHiddenTask(location, testThread, bridge, value, doneEvent.get()); |
| doneEvent->wait(); |
| } |
| |
| class MockImageBuffer : public ImageBuffer { |
| public: |
| MockImageBuffer() |
| : ImageBuffer(WTF::wrapUnique( |
| new UnacceleratedImageBufferSurface(IntSize(1, 1)))) {} |
| |
| MOCK_CONST_METHOD1(resetCanvas, void(PaintCanvas*)); |
| |
| virtual ~MockImageBuffer() {} |
| }; |
| |
| #if CANVAS2D_HIBERNATION_ENABLED |
| TEST_F(Canvas2DLayerBridgeTest, HibernationLifeCycle) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationLifeCycle) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationStartedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) |
| .WillOnce(testing::Invoke(hibernationStartedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| hibernationStartedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_TRUE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Test exiting hibernation |
| EXPECT_CALL( |
| *mockLoggerPtr, |
| reportHibernationEvent(Canvas2DLayerBridge::HibernationEndedNormally)); |
| postAndWaitSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), |
| false); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_TRUE(bridge->isAccelerated()); |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Tear down the bridge on the thread so that 'bridge' can go out of scope |
| // without crashing due to thread checks |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED |
| TEST_F(Canvas2DLayerBridgeTest, HibernationReEntry) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationReEntry) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationStartedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) |
| .WillOnce(testing::Invoke(hibernationStartedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| // Toggle visibility before the task that enters hibernation gets a |
| // chance to run. |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), false); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| |
| hibernationStartedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_TRUE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Test exiting hibernation |
| EXPECT_CALL( |
| *mockLoggerPtr, |
| reportHibernationEvent(Canvas2DLayerBridge::HibernationEndedNormally)); |
| postAndWaitSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), |
| false); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_TRUE(bridge->isAccelerated()); |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Tear down the bridge on the thread so that 'bridge' can go out of scope |
| // without crashing due to thread checks |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED |
| TEST_F(Canvas2DLayerBridgeTest, |
| HibernationLifeCycleWithDeferredRenderingDisabled) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, |
| DISABLED_HibernationLifeCycleWithDeferredRenderingDisabled) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| bridge->disableDeferral(DisableDeferralReasonUnknown); |
| MockImageBuffer mockImageBuffer; |
| EXPECT_CALL(mockImageBuffer, resetCanvas(_)).Times(AnyNumber()); |
| bridge->setImageBuffer(&mockImageBuffer); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationStartedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) |
| .WillOnce(testing::Invoke(hibernationStartedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| hibernationStartedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| ::testing::Mock::VerifyAndClearExpectations(&mockImageBuffer); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_TRUE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Test exiting hibernation |
| EXPECT_CALL( |
| *mockLoggerPtr, |
| reportHibernationEvent(Canvas2DLayerBridge::HibernationEndedNormally)); |
| EXPECT_CALL(mockImageBuffer, resetCanvas(_)) |
| .Times(AtLeast(1)); // Because deferred rendering is disabled |
| postAndWaitSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), |
| false); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| ::testing::Mock::VerifyAndClearExpectations(&mockImageBuffer); |
| EXPECT_TRUE(bridge->isAccelerated()); |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Tear down the bridge on the thread so that 'bridge' can go out of scope |
| // without crashing due to thread checks |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| void runRenderingTask(Canvas2DLayerBridge* bridge, WaitableEvent* doneEvent) { |
| bridge->didDraw(FloatRect(0, 0, 1, 1)); |
| bridge->finalizeFrame(); |
| bridge->flush(); |
| doneEvent->signal(); |
| } |
| |
| void postAndWaitRenderingTask(const WebTraceLocation& location, |
| WebThread* testThread, |
| Canvas2DLayerBridge* bridge) { |
| std::unique_ptr<WaitableEvent> doneEvent = WTF::makeUnique<WaitableEvent>(); |
| testThread->getWebTaskRunner()->postTask( |
| location, |
| crossThreadBind(&runRenderingTask, crossThreadUnretained(bridge), |
| crossThreadUnretained(doneEvent.get()))); |
| doneEvent->wait(); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED && CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU |
| TEST_F(Canvas2DLayerBridgeTest, BackgroundRenderingWhileHibernating) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_BackgroundRenderingWhileHibernating) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationStartedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) |
| .WillOnce(testing::Invoke(hibernationStartedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| hibernationStartedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_TRUE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Rendering in the background -> temp switch to SW |
| EXPECT_CALL(*mockLoggerPtr, |
| reportHibernationEvent( |
| Canvas2DLayerBridge:: |
| HibernationEndedWithSwitchToBackgroundRendering)); |
| postAndWaitRenderingTask(BLINK_FROM_HERE, testThread.get(), bridge.get()); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Unhide |
| postAndWaitSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), |
| false); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_TRUE( |
| bridge->isAccelerated()); // Becoming visible causes switch back to GPU |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Tear down the bridge on the thread so that 'bridge' can go out of scope |
| // without crashing due to thread checks |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED && CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU |
| TEST_F(Canvas2DLayerBridgeTest, |
| BackgroundRenderingWhileHibernatingWithDeferredRenderingDisabled) |
| #else |
| TEST_F( |
| Canvas2DLayerBridgeTest, |
| DISABLED_BackgroundRenderingWhileHibernatingWithDeferredRenderingDisabled) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| MockImageBuffer mockImageBuffer; |
| EXPECT_CALL(mockImageBuffer, resetCanvas(_)).Times(AnyNumber()); |
| bridge->setImageBuffer(&mockImageBuffer); |
| bridge->disableDeferral(DisableDeferralReasonUnknown); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationStartedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) |
| .WillOnce(testing::Invoke(hibernationStartedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| hibernationStartedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| ::testing::Mock::VerifyAndClearExpectations(&mockImageBuffer); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_TRUE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Rendering in the background -> temp switch to SW |
| EXPECT_CALL(*mockLoggerPtr, |
| reportHibernationEvent( |
| Canvas2DLayerBridge:: |
| HibernationEndedWithSwitchToBackgroundRendering)); |
| EXPECT_CALL(mockImageBuffer, resetCanvas(_)).Times(AtLeast(1)); |
| postAndWaitRenderingTask(BLINK_FROM_HERE, testThread.get(), bridge.get()); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| ::testing::Mock::VerifyAndClearExpectations(&mockImageBuffer); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Unhide |
| EXPECT_CALL(mockImageBuffer, resetCanvas(_)).Times(AtLeast(1)); |
| postAndWaitSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), |
| false); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| ::testing::Mock::VerifyAndClearExpectations(&mockImageBuffer); |
| EXPECT_TRUE( |
| bridge->isAccelerated()); // Becoming visible causes switch back to GPU |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Tear down the bridge on the thread so that 'bridge' can go out of scope |
| // without crashing due to thread checks |
| EXPECT_CALL(mockImageBuffer, resetCanvas(_)).Times(AnyNumber()); |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED && CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU |
| TEST_F(Canvas2DLayerBridgeTest, DisableDeferredRenderingWhileHibernating) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, |
| DISABLED_DisableDeferredRenderingWhileHibernating) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| MockImageBuffer mockImageBuffer; |
| EXPECT_CALL(mockImageBuffer, resetCanvas(_)).Times(AnyNumber()); |
| bridge->setImageBuffer(&mockImageBuffer); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationStartedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) |
| .WillOnce(testing::Invoke(hibernationStartedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| hibernationStartedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| ::testing::Mock::VerifyAndClearExpectations(&mockImageBuffer); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_TRUE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Disable deferral while background rendering |
| EXPECT_CALL(*mockLoggerPtr, |
| reportHibernationEvent( |
| Canvas2DLayerBridge:: |
| HibernationEndedWithSwitchToBackgroundRendering)); |
| EXPECT_CALL(mockImageBuffer, resetCanvas(_)).Times(AtLeast(1)); |
| bridge->disableDeferral(DisableDeferralReasonUnknown); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| ::testing::Mock::VerifyAndClearExpectations(&mockImageBuffer); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Unhide |
| EXPECT_CALL(mockImageBuffer, resetCanvas(_)).Times(AtLeast(1)); |
| postAndWaitSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), |
| false); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| ::testing::Mock::VerifyAndClearExpectations(&mockImageBuffer); |
| EXPECT_TRUE( |
| bridge->isAccelerated()); // Becoming visible causes switch back to GPU |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Tear down the bridge on the thread so that 'bridge' can go out of scope |
| // without crashing due to thread checks |
| EXPECT_CALL(mockImageBuffer, resetCanvas(_)).Times(AnyNumber()); |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED |
| TEST_F(Canvas2DLayerBridgeTest, TeardownWhileHibernating) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_TeardownWhileHibernating) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationStartedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) |
| .WillOnce(testing::Invoke(hibernationStartedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| hibernationStartedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_TRUE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Tear down the bridge while hibernating |
| EXPECT_CALL(*mockLoggerPtr, |
| reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationEndedWithTeardown)); |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED |
| TEST_F(Canvas2DLayerBridgeTest, SnapshotWhileHibernating) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_SnapshotWhileHibernating) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationStartedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) |
| .WillOnce(testing::Invoke(hibernationStartedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| hibernationStartedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_TRUE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Take a snapshot and verify that it is not accelerated due to hibernation |
| sk_sp<SkImage> image = |
| bridge->newImageSnapshot(PreferAcceleration, SnapshotReasonUnitTests); |
| EXPECT_FALSE(image->isTextureBacked()); |
| image.reset(); |
| |
| // Verify that taking a snapshot did not affect the state of bridge |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_TRUE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // End hibernation normally |
| std::unique_ptr<WaitableEvent> hibernationEndedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL( |
| *mockLoggerPtr, |
| reportHibernationEvent(Canvas2DLayerBridge::HibernationEndedNormally)) |
| .WillOnce(testing::InvokeWithoutArgs(hibernationEndedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), false); |
| hibernationEndedEvent->wait(); |
| |
| // Tear down the bridge while hibernating |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED |
| TEST_F(Canvas2DLayerBridgeTest, TeardownWhileHibernationIsPending) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_TeardownWhileHibernationIsPending) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationScheduledEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true, |
| hibernationScheduledEvent.get()); |
| postDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| // In production, we would expect a |
| // HibernationAbortedDueToDestructionWhileHibernatePending event to be |
| // fired, but that signal is lost in the unit test due to no longer having |
| // a bridge to hold the mockLogger. |
| hibernationScheduledEvent->wait(); |
| // Once we know the hibernation task is scheduled, we can schedule a fence. |
| // Assuming tasks are guaranteed to run in the order they were |
| // submitted, this fence will guarantee the attempt to hibernate runs to |
| // completion before the thread is destroyed. |
| // This test passes by not crashing, which proves that the WeakPtr logic |
| // is sound. |
| std::unique_ptr<WaitableEvent> fenceEvent = WTF::makeUnique<WaitableEvent>(); |
| testThread->getWebTaskRunner()->postTask( |
| BLINK_FROM_HERE, |
| WTF::bind(&WaitableEvent::signal, WTF::unretained(fenceEvent.get()))); |
| fenceEvent->wait(); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED |
| TEST_F(Canvas2DLayerBridgeTest, HibernationAbortedDueToPendingTeardown) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationAbortedDueToPendingTeardown) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationAbortedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL( |
| *mockLoggerPtr, |
| reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationAbortedDueToPendingDestruction)) |
| .WillOnce(testing::InvokeWithoutArgs(hibernationAbortedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| testThread->getWebTaskRunner()->postTask( |
| BLINK_FROM_HERE, crossThreadBind(&Canvas2DLayerBridge::beginDestruction, |
| crossThreadUnretained(bridge.get()))); |
| hibernationAbortedEvent->wait(); |
| |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| |
| // Tear down bridge on thread |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED |
| TEST_F(Canvas2DLayerBridgeTest, HibernationAbortedDueToVisibilityChange) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, |
| DISABLED_HibernationAbortedDueToVisibilityChange) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationAbortedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, |
| reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationAbortedDueToVisibilityChange)) |
| .WillOnce(testing::InvokeWithoutArgs(hibernationAbortedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), false); |
| hibernationAbortedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_TRUE(bridge->isAccelerated()); |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Tear down the bridge on the thread so that 'bridge' can go out of scope |
| // without crashing due to thread checks |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED |
| TEST_F(Canvas2DLayerBridgeTest, HibernationAbortedDueToLostContext) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationAbortedDueToLostContext) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| gl.setIsContextLost(true); |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationAbortedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, |
| reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationAbortedDueGpuContextLoss)) |
| .WillOnce(testing::InvokeWithoutArgs(hibernationAbortedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| hibernationAbortedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_FALSE(bridge->isHibernating()); |
| |
| // Tear down the bridge on the thread so that 'bridge' can go out of scope |
| // without crashing due to thread checks |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED |
| TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxWhileHibernating) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_PrepareMailboxWhileHibernating) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationStartedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) |
| .WillOnce(testing::Invoke(hibernationStartedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| hibernationStartedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| |
| // Test prepareMailbox while hibernating |
| cc::TextureMailbox textureMailbox; |
| std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; |
| EXPECT_FALSE( |
| bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback)); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Tear down the bridge on the thread so that 'bridge' can go out of scope |
| // without crashing due to thread checks |
| EXPECT_CALL(*mockLoggerPtr, |
| reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationEndedWithTeardown)); |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if CANVAS2D_HIBERNATION_ENABLED && CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU |
| TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxWhileBackgroundRendering) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_PrepareMailboxWhileBackgroundRendering) |
| #endif |
| { |
| FakeGLES2Interface gl; |
| std::unique_ptr<WebThread> testThread = |
| WTF::wrapUnique(Platform::current()->createThread("TestThread")); |
| |
| // The Canvas2DLayerBridge has to be created on the thread that will use it |
| // to avoid WeakPtr thread check issues. |
| Canvas2DLayerBridgePtr bridge; |
| postAndWaitCreateBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge, &gl, |
| this); |
| |
| // Register an alternate Logger for tracking hibernation events |
| std::unique_ptr<MockLogger> mockLogger = WTF::wrapUnique(new MockLogger); |
| MockLogger* mockLoggerPtr = mockLogger.get(); |
| bridge->setLoggerForTesting(std::move(mockLogger)); |
| |
| // Test entering hibernation |
| std::unique_ptr<WaitableEvent> hibernationStartedEvent = |
| WTF::makeUnique<WaitableEvent>(); |
| EXPECT_CALL(*mockLoggerPtr, reportHibernationEvent( |
| Canvas2DLayerBridge::HibernationScheduled)); |
| EXPECT_CALL(*mockLoggerPtr, didStartHibernating()) |
| .WillOnce(testing::Invoke(hibernationStartedEvent.get(), |
| &WaitableEvent::signal)); |
| postSetIsHiddenTask(BLINK_FROM_HERE, testThread.get(), bridge.get(), true); |
| hibernationStartedEvent->wait(); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| |
| // Rendering in the background -> temp switch to SW |
| EXPECT_CALL(*mockLoggerPtr, |
| reportHibernationEvent( |
| Canvas2DLayerBridge:: |
| HibernationEndedWithSwitchToBackgroundRendering)); |
| postAndWaitRenderingTask(BLINK_FROM_HERE, testThread.get(), bridge.get()); |
| ::testing::Mock::VerifyAndClearExpectations(mockLoggerPtr); |
| EXPECT_FALSE(bridge->isAccelerated()); |
| EXPECT_FALSE(bridge->isHibernating()); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Test prepareMailbox while background rendering |
| cc::TextureMailbox textureMailbox; |
| std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; |
| EXPECT_FALSE( |
| bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback)); |
| EXPECT_TRUE(bridge->checkSurfaceValid()); |
| |
| // Tear down the bridge on the thread so that 'bridge' can go out of scope |
| // without crashing due to thread checks |
| postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); |
| } |
| |
| #if USE_IOSURFACE_FOR_2D_CANVAS |
| TEST_F(Canvas2DLayerBridgeTest, DeleteIOSurfaceAfterTeardown) |
| #else |
| TEST_F(Canvas2DLayerBridgeTest, DISABLED_DeleteIOSurfaceAfterTeardown) |
| #endif |
| { |
| FakeGLES2InterfaceWithImageSupport gl; |
| ScopedTestingPlatformSupport<FakePlatformSupport> platform; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| |
| cc::TextureMailbox textureMailbox; |
| std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; |
| |
| { |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::ForceAccelerationForTesting, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback); |
| } |
| |
| bool lostResource = false; |
| releaseCallback->Run(gpu::SyncToken(), lostResource); |
| |
| EXPECT_EQ(1u, gl.createImageCount()); |
| EXPECT_EQ(1u, gl.destroyImageCount()); |
| } |
| |
| class FlushMockGLES2Interface : public gpu::gles2::GLES2InterfaceStub { |
| public: |
| MOCK_METHOD0(Flush, void()); |
| }; |
| |
| TEST_F(Canvas2DLayerBridgeTest, NoUnnecessaryFlushes) { |
| FlushMockGLES2Interface gl; |
| std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = |
| WTF::wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); |
| |
| EXPECT_CALL(gl, Flush()).Times(0); |
| Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge( |
| std::move(contextProvider), IntSize(300, 150), 0, NonOpaque, |
| Canvas2DLayerBridge::ForceAccelerationForTesting, |
| gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType))); |
| EXPECT_FALSE(bridge->hasRecordedDrawCommands()); |
| ::testing::Mock::VerifyAndClearExpectations(&gl); |
| |
| EXPECT_CALL(gl, Flush()).Times(0); |
| bridge->didDraw(FloatRect(0, 0, 1, 1)); |
| EXPECT_TRUE(bridge->hasRecordedDrawCommands()); |
| ::testing::Mock::VerifyAndClearExpectations(&gl); |
| |
| EXPECT_CALL(gl, Flush()).Times(1); |
| bridge->flushGpu(); |
| EXPECT_FALSE(bridge->hasRecordedDrawCommands()); |
| ::testing::Mock::VerifyAndClearExpectations(&gl); |
| |
| EXPECT_CALL(gl, Flush()).Times(0); |
| bridge->didDraw(FloatRect(0, 0, 1, 1)); |
| EXPECT_TRUE(bridge->hasRecordedDrawCommands()); |
| ::testing::Mock::VerifyAndClearExpectations(&gl); |
| |
| EXPECT_CALL(gl, Flush()).Times(1); |
| bridge->flushGpu(); |
| EXPECT_FALSE(bridge->hasRecordedDrawCommands()); |
| ::testing::Mock::VerifyAndClearExpectations(&gl); |
| |
| // No flush because already flushed since last draw |
| EXPECT_CALL(gl, Flush()).Times(0); |
| bridge->flushGpu(); |
| EXPECT_FALSE(bridge->hasRecordedDrawCommands()); |
| ::testing::Mock::VerifyAndClearExpectations(&gl); |
| |
| EXPECT_CALL(gl, Flush()).Times(0); |
| bridge->didDraw(FloatRect(0, 0, 1, 1)); |
| EXPECT_TRUE(bridge->hasRecordedDrawCommands()); |
| ::testing::Mock::VerifyAndClearExpectations(&gl); |
| |
| // Flushes recording, but not the gpu |
| EXPECT_CALL(gl, Flush()).Times(0); |
| bridge->flush(); |
| EXPECT_FALSE(bridge->hasRecordedDrawCommands()); |
| ::testing::Mock::VerifyAndClearExpectations(&gl); |
| |
| EXPECT_CALL(gl, Flush()).Times(1); |
| bridge->flushGpu(); |
| EXPECT_FALSE(bridge->hasRecordedDrawCommands()); |
| ::testing::Mock::VerifyAndClearExpectations(&gl); |
| } |
| |
| } // namespace blink |