blob: 3d8f4d42ac78fd7f4ff02a85ee9d83a85145de3a [file] [log] [blame]
/*
* 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/test/FakeGLES2Interface.h"
#include "platform/graphics/test/FakeWebGraphicsContext3DProvider.h"
#include "platform/scheduler/child/web_scheduler.h"
#include "platform/testing/TestingPlatformSupport.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/RefPtr.h"
#include "public/platform/Platform.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/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/gl/GrGLTypes.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> layer_bridge)
: layer_bridge_(std::move(layer_bridge)) {}
~Canvas2DLayerBridgePtr() { Clear(); }
void Clear() {
if (layer_bridge_) {
layer_bridge_->BeginDestruction();
layer_bridge_.Clear();
}
}
void operator=(PassRefPtr<Canvas2DLayerBridge> layer_bridge) {
DCHECK(!layer_bridge_);
layer_bridge_ = std::move(layer_bridge);
}
Canvas2DLayerBridge* operator->() { return layer_bridge_.Get(); }
Canvas2DLayerBridge* Get() { return layer_bridge_.Get(); }
private:
RefPtr<Canvas2DLayerBridge> layer_bridge_;
};
class FakeGLES2InterfaceWithImageSupport : public FakeGLES2Interface {
public:
GLuint CreateImageCHROMIUM(ClientBuffer, GLsizei, GLsizei, GLenum) override {
return ++create_image_count_;
}
void DestroyImageCHROMIUM(GLuint) override { ++destroy_image_count_; }
GLuint CreateImageCount() { return create_image_count_; }
GLuint DestroyImageCount() { return destroy_image_count_; }
private:
GLuint create_image_count_ = 0;
GLuint destroy_image_count_ = 0;
};
class FakePlatformSupport : public TestingPlatformSupport {
gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override {
return &test_gpu_memory_buffer_manager_;
}
private:
cc::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
};
} // anonymous namespace
class Canvas2DLayerBridgeTest : public Test {
public:
PassRefPtr<Canvas2DLayerBridge> MakeBridge(
std::unique_ptr<FakeWebGraphicsContext3DProvider> provider,
const IntSize& size,
Canvas2DLayerBridge::AccelerationMode acceleration_mode) {
RefPtr<Canvas2DLayerBridge> bridge = AdoptRef(
new Canvas2DLayerBridge(std::move(provider), size, 0, kNonOpaque,
acceleration_mode, CanvasColorParams()));
bridge->DontUseIdleSchedulingForTesting();
return bridge.Release();
}
protected:
void FullLifecycleTest() {
FakeGLES2Interface gl;
std::unique_ptr<FakeWebGraphicsContext3DProvider> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kDisableAcceleration, CanvasColorParams())));
const GrGLTextureInfo* texture_info =
skia::GrBackendObjectToGrGLTextureInfo(
bridge
->NewImageSnapshot(kPreferAcceleration,
kSnapshotReasonUnitTests)
->getTextureHandle(true));
EXPECT_EQ(texture_info, nullptr);
bridge.Clear();
}
void FallbackToSoftwareIfContextLost() {
FakeGLES2Interface gl;
std::unique_ptr<FakeWebGraphicsContext3DProvider> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
gl.SetIsContextLost(true);
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams())));
EXPECT_TRUE(bridge->CheckSurfaceValid());
EXPECT_FALSE(bridge->IsAccelerated());
}
void FallbackToSoftwareOnFailedTextureAlloc() {
{
// No fallback case.
FakeGLES2Interface gl;
std::unique_ptr<FakeWebGraphicsContext3DProvider> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams())));
EXPECT_TRUE(bridge->CheckSurfaceValid());
EXPECT_TRUE(bridge->IsAccelerated());
sk_sp<SkImage> snapshot = bridge->NewImageSnapshot(
kPreferAcceleration, kSnapshotReasonUnitTests);
EXPECT_TRUE(bridge->IsAccelerated());
EXPECT_TRUE(snapshot->isTextureBacked());
}
{
// Fallback case.
FakeGLES2Interface gl;
std::unique_ptr<FakeWebGraphicsContext3DProvider> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
GrContext* gr = context_provider->GetGrContext();
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams())));
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(
kPreferAcceleration, kSnapshotReasonUnitTests);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_FALSE(snapshot->isTextureBacked());
}
}
void NoDrawOnContextLostTest() {
FakeGLES2Interface gl;
std::unique_ptr<FakeWebGraphicsContext3DProvider> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kForceAccelerationForTesting,
CanvasColorParams())));
EXPECT_TRUE(bridge->CheckSurfaceValid());
PaintFlags flags;
uint32_t gen_id = bridge->GetOrCreateSurface()->generationID();
bridge->Canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
EXPECT_EQ(gen_id, bridge->GetOrCreateSurface()->generationID());
gl.SetIsContextLost(true);
EXPECT_EQ(gen_id, bridge->GetOrCreateSurface()->generationID());
bridge->Canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
EXPECT_EQ(gen_id, 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> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kForceAccelerationForTesting,
CanvasColorParams())));
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 texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
EXPECT_FALSE(
bridge->PrepareTextureMailbox(&texture_mailbox, &release_callback));
}
void PrepareMailboxWhenContextIsLostWithFailedRestore() {
FakeGLES2Interface gl;
std::unique_ptr<FakeWebGraphicsContext3DProvider> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kForceAccelerationForTesting,
CanvasColorParams())));
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 texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
EXPECT_FALSE(
bridge->PrepareTextureMailbox(&texture_mailbox, &release_callback));
}
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> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kForceAccelerationForTesting,
CanvasColorParams())));
cc::TextureMailbox texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
EXPECT_TRUE(
bridge->PrepareTextureMailbox(&texture_mailbox, &release_callback));
bool lost_resource = true;
release_callback->Run(gpu::SyncToken(), lost_resource);
}
// Retry with mailbox released while bridge destruction is in progress.
{
FakeGLES2Interface gl;
std::unique_ptr<FakeWebGraphicsContext3DProvider> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
cc::TextureMailbox texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
{
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kForceAccelerationForTesting,
CanvasColorParams())));
bridge->PrepareTextureMailbox(&texture_mailbox, &release_callback);
// |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 lost_resource = 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.
release_callback->Run(gpu::SyncToken(), lost_resource);
}
}
void AccelerationHintTest() {
{
FakeGLES2Interface gl;
std::unique_ptr<FakeWebGraphicsContext3DProvider> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 300), 0, kNonOpaque,
Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams())));
PaintFlags flags;
bridge->Canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
sk_sp<SkImage> image = bridge->NewImageSnapshot(kPreferAcceleration,
kSnapshotReasonUnitTests);
EXPECT_TRUE(bridge->CheckSurfaceValid());
EXPECT_TRUE(bridge->IsAccelerated());
}
{
FakeGLES2Interface gl;
std::unique_ptr<FakeWebGraphicsContext3DProvider> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 300), 0, kNonOpaque,
Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams())));
PaintFlags flags;
bridge->Canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
sk_sp<SkImage> image = bridge->NewImageSnapshot(kPreferNoAcceleration,
kSnapshotReasonUnitTests);
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* bridge_ptr,
gpu::gles2::GLES2Interface* gl,
Canvas2DLayerBridgeTest* test_host,
WaitableEvent* done_event) {
std::unique_ptr<FakeWebGraphicsContext3DProvider> context_provider =
WTF::MakeUnique<FakeWebGraphicsContext3DProvider>(gl);
*bridge_ptr =
test_host->MakeBridge(std::move(context_provider), IntSize(300, 300),
Canvas2DLayerBridge::kEnableAcceleration);
// draw+flush to trigger the creation of a GPU surface
(*bridge_ptr)->DidDraw(FloatRect(0, 0, 1, 1));
(*bridge_ptr)->FinalizeFrame();
(*bridge_ptr)->Flush();
done_event->Signal();
}
void PostAndWaitCreateBridgeTask(const WebTraceLocation& location,
WebThread* test_thread,
Canvas2DLayerBridgePtr* bridge_ptr,
gpu::gles2::GLES2Interface* gl,
Canvas2DLayerBridgeTest* test_host) {
std::unique_ptr<WaitableEvent> bridge_created_event =
WTF::MakeUnique<WaitableEvent>();
test_thread->GetWebTaskRunner()->PostTask(
location, CrossThreadBind(
&RunCreateBridgeTask, CrossThreadUnretained(bridge_ptr),
CrossThreadUnretained(gl), CrossThreadUnretained(test_host),
CrossThreadUnretained(bridge_created_event.get())));
bridge_created_event->Wait();
}
void RunDestroyBridgeTask(Canvas2DLayerBridgePtr* bridge_ptr,
WaitableEvent* done_event) {
bridge_ptr->Clear();
if (done_event)
done_event->Signal();
}
void PostDestroyBridgeTask(const WebTraceLocation& location,
WebThread* test_thread,
Canvas2DLayerBridgePtr* bridge_ptr) {
test_thread->GetWebTaskRunner()->PostTask(
location, CrossThreadBind(&RunDestroyBridgeTask,
CrossThreadUnretained(bridge_ptr), nullptr));
}
void PostAndWaitDestroyBridgeTask(const WebTraceLocation& location,
WebThread* test_thread,
Canvas2DLayerBridgePtr* bridge_ptr) {
std::unique_ptr<WaitableEvent> bridge_destroyed_event =
WTF::MakeUnique<WaitableEvent>();
test_thread->GetWebTaskRunner()->PostTask(
location,
CrossThreadBind(&RunDestroyBridgeTask, CrossThreadUnretained(bridge_ptr),
CrossThreadUnretained(bridge_destroyed_event.get())));
bridge_destroyed_event->Wait();
}
void RunSetIsHiddenTask(Canvas2DLayerBridge* bridge,
bool value,
WaitableEvent* done_event) {
bridge->SetIsHidden(value);
if (done_event)
done_event->Signal();
}
void PostSetIsHiddenTask(const WebTraceLocation& location,
WebThread* test_thread,
Canvas2DLayerBridge* bridge,
bool value,
WaitableEvent* done_event = nullptr) {
test_thread->GetWebTaskRunner()->PostTask(
location,
CrossThreadBind(&RunSetIsHiddenTask, CrossThreadUnretained(bridge), value,
CrossThreadUnretained(done_event)));
}
void PostAndWaitSetIsHiddenTask(const WebTraceLocation& location,
WebThread* test_thread,
Canvas2DLayerBridge* bridge,
bool value) {
std::unique_ptr<WaitableEvent> done_event = WTF::MakeUnique<WaitableEvent>();
PostSetIsHiddenTask(location, test_thread, bridge, value, done_event.get());
done_event->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> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_started_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr, DidStartHibernating())
.WillOnce(testing::Invoke(hibernation_started_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_started_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_TRUE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Test exiting hibernation
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationEndedNormally));
PostAndWaitSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(),
false);
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
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, test_thread.get(), &bridge);
}
#if CANVAS2D_HIBERNATION_ENABLED
TEST_F(Canvas2DLayerBridgeTest, HibernationReEntry)
#else
TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationReEntry)
#endif
{
FakeGLES2Interface gl;
std::unique_ptr<WebThread> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_started_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr, DidStartHibernating())
.WillOnce(testing::Invoke(hibernation_started_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
// Toggle visibility before the task that enters hibernation gets a
// chance to run.
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), false);
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_started_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_TRUE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Test exiting hibernation
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationEndedNormally));
PostAndWaitSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(),
false);
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
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, test_thread.get(), &bridge);
}
#if CANVAS2D_HIBERNATION_ENABLED
TEST_F(Canvas2DLayerBridgeTest,
HibernationLifeCycleWithDeferredRenderingDisabled)
#else
TEST_F(Canvas2DLayerBridgeTest,
DISABLED_HibernationLifeCycleWithDeferredRenderingDisabled)
#endif
{
FakeGLES2Interface gl;
std::unique_ptr<WebThread> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
bridge->DisableDeferral(kDisableDeferralReasonUnknown);
MockImageBuffer mock_image_buffer;
EXPECT_CALL(mock_image_buffer, ResetCanvas(_)).Times(AnyNumber());
bridge->SetImageBuffer(&mock_image_buffer);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_started_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr, DidStartHibernating())
.WillOnce(testing::Invoke(hibernation_started_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_started_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
::testing::Mock::VerifyAndClearExpectations(&mock_image_buffer);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_TRUE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Test exiting hibernation
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationEndedNormally));
EXPECT_CALL(mock_image_buffer, ResetCanvas(_))
.Times(AtLeast(1)); // Because deferred rendering is disabled
PostAndWaitSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(),
false);
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
::testing::Mock::VerifyAndClearExpectations(&mock_image_buffer);
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, test_thread.get(), &bridge);
}
void RunRenderingTask(Canvas2DLayerBridge* bridge, WaitableEvent* done_event) {
bridge->DidDraw(FloatRect(0, 0, 1, 1));
bridge->FinalizeFrame();
bridge->Flush();
done_event->Signal();
}
void PostAndWaitRenderingTask(const WebTraceLocation& location,
WebThread* test_thread,
Canvas2DLayerBridge* bridge) {
std::unique_ptr<WaitableEvent> done_event = WTF::MakeUnique<WaitableEvent>();
test_thread->GetWebTaskRunner()->PostTask(
location,
CrossThreadBind(&RunRenderingTask, CrossThreadUnretained(bridge),
CrossThreadUnretained(done_event.get())));
done_event->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> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_started_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr, DidStartHibernating())
.WillOnce(testing::Invoke(hibernation_started_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_started_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_TRUE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Rendering in the background -> temp switch to SW
EXPECT_CALL(*mock_logger_ptr,
ReportHibernationEvent(
Canvas2DLayerBridge::
kHibernationEndedWithSwitchToBackgroundRendering));
PostAndWaitRenderingTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get());
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_FALSE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Unhide
PostAndWaitSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(),
false);
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
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, test_thread.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> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
MockImageBuffer mock_image_buffer;
EXPECT_CALL(mock_image_buffer, ResetCanvas(_)).Times(AnyNumber());
bridge->SetImageBuffer(&mock_image_buffer);
bridge->DisableDeferral(kDisableDeferralReasonUnknown);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_started_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr, DidStartHibernating())
.WillOnce(testing::Invoke(hibernation_started_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_started_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
::testing::Mock::VerifyAndClearExpectations(&mock_image_buffer);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_TRUE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Rendering in the background -> temp switch to SW
EXPECT_CALL(*mock_logger_ptr,
ReportHibernationEvent(
Canvas2DLayerBridge::
kHibernationEndedWithSwitchToBackgroundRendering));
EXPECT_CALL(mock_image_buffer, ResetCanvas(_)).Times(AtLeast(1));
PostAndWaitRenderingTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get());
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
::testing::Mock::VerifyAndClearExpectations(&mock_image_buffer);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_FALSE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Unhide
EXPECT_CALL(mock_image_buffer, ResetCanvas(_)).Times(AtLeast(1));
PostAndWaitSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(),
false);
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
::testing::Mock::VerifyAndClearExpectations(&mock_image_buffer);
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(mock_image_buffer, ResetCanvas(_)).Times(AnyNumber());
PostAndWaitDestroyBridgeTask(BLINK_FROM_HERE, test_thread.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> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
MockImageBuffer mock_image_buffer;
EXPECT_CALL(mock_image_buffer, ResetCanvas(_)).Times(AnyNumber());
bridge->SetImageBuffer(&mock_image_buffer);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_started_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr, DidStartHibernating())
.WillOnce(testing::Invoke(hibernation_started_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_started_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
::testing::Mock::VerifyAndClearExpectations(&mock_image_buffer);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_TRUE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Disable deferral while background rendering
EXPECT_CALL(*mock_logger_ptr,
ReportHibernationEvent(
Canvas2DLayerBridge::
kHibernationEndedWithSwitchToBackgroundRendering));
EXPECT_CALL(mock_image_buffer, ResetCanvas(_)).Times(AtLeast(1));
bridge->DisableDeferral(kDisableDeferralReasonUnknown);
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
::testing::Mock::VerifyAndClearExpectations(&mock_image_buffer);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_FALSE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Unhide
EXPECT_CALL(mock_image_buffer, ResetCanvas(_)).Times(AtLeast(1));
PostAndWaitSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(),
false);
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
::testing::Mock::VerifyAndClearExpectations(&mock_image_buffer);
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(mock_image_buffer, ResetCanvas(_)).Times(AnyNumber());
PostAndWaitDestroyBridgeTask(BLINK_FROM_HERE, test_thread.get(), &bridge);
}
#if CANVAS2D_HIBERNATION_ENABLED
TEST_F(Canvas2DLayerBridgeTest, TeardownWhileHibernating)
#else
TEST_F(Canvas2DLayerBridgeTest, DISABLED_TeardownWhileHibernating)
#endif
{
FakeGLES2Interface gl;
std::unique_ptr<WebThread> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_started_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr, DidStartHibernating())
.WillOnce(testing::Invoke(hibernation_started_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_started_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_TRUE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Tear down the bridge while hibernating
EXPECT_CALL(*mock_logger_ptr,
ReportHibernationEvent(
Canvas2DLayerBridge::kHibernationEndedWithTeardown));
PostAndWaitDestroyBridgeTask(BLINK_FROM_HERE, test_thread.get(), &bridge);
}
#if CANVAS2D_HIBERNATION_ENABLED
TEST_F(Canvas2DLayerBridgeTest, SnapshotWhileHibernating)
#else
TEST_F(Canvas2DLayerBridgeTest, DISABLED_SnapshotWhileHibernating)
#endif
{
FakeGLES2Interface gl;
std::unique_ptr<WebThread> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_started_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr, DidStartHibernating())
.WillOnce(testing::Invoke(hibernation_started_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_started_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
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(kPreferAcceleration, kSnapshotReasonUnitTests);
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> hibernation_ended_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationEndedNormally))
.WillOnce(testing::InvokeWithoutArgs(hibernation_ended_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), false);
hibernation_ended_event->Wait();
// Tear down the bridge while hibernating
PostAndWaitDestroyBridgeTask(BLINK_FROM_HERE, test_thread.get(), &bridge);
}
#if CANVAS2D_HIBERNATION_ENABLED
TEST_F(Canvas2DLayerBridgeTest, TeardownWhileHibernationIsPending)
#else
TEST_F(Canvas2DLayerBridgeTest, DISABLED_TeardownWhileHibernationIsPending)
#endif
{
FakeGLES2Interface gl;
std::unique_ptr<WebThread> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_scheduled_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true,
hibernation_scheduled_event.get());
PostDestroyBridgeTask(BLINK_FROM_HERE, test_thread.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.
hibernation_scheduled_event->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> fence_event = WTF::MakeUnique<WaitableEvent>();
test_thread->GetWebTaskRunner()->PostTask(
BLINK_FROM_HERE,
WTF::Bind(&WaitableEvent::Signal, WTF::Unretained(fence_event.get())));
fence_event->Wait();
}
#if CANVAS2D_HIBERNATION_ENABLED
TEST_F(Canvas2DLayerBridgeTest, HibernationAbortedDueToPendingTeardown)
#else
TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationAbortedDueToPendingTeardown)
#endif
{
FakeGLES2Interface gl;
std::unique_ptr<WebThread> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_aborted_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(
Canvas2DLayerBridge::kHibernationAbortedDueToPendingDestruction))
.WillOnce(testing::InvokeWithoutArgs(hibernation_aborted_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
test_thread->GetWebTaskRunner()->PostTask(
BLINK_FROM_HERE, CrossThreadBind(&Canvas2DLayerBridge::BeginDestruction,
CrossThreadUnretained(bridge.Get())));
hibernation_aborted_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
// Tear down bridge on thread
PostAndWaitDestroyBridgeTask(BLINK_FROM_HERE, test_thread.get(), &bridge);
}
#if CANVAS2D_HIBERNATION_ENABLED
TEST_F(Canvas2DLayerBridgeTest, HibernationAbortedDueToVisibilityChange)
#else
TEST_F(Canvas2DLayerBridgeTest,
DISABLED_HibernationAbortedDueToVisibilityChange)
#endif
{
FakeGLES2Interface gl;
std::unique_ptr<WebThread> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_aborted_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(
Canvas2DLayerBridge::kHibernationAbortedDueToVisibilityChange))
.WillOnce(testing::InvokeWithoutArgs(hibernation_aborted_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), false);
hibernation_aborted_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
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, test_thread.get(), &bridge);
}
#if CANVAS2D_HIBERNATION_ENABLED
TEST_F(Canvas2DLayerBridgeTest, HibernationAbortedDueToLostContext)
#else
TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationAbortedDueToLostContext)
#endif
{
FakeGLES2Interface gl;
std::unique_ptr<WebThread> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
gl.SetIsContextLost(true);
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_aborted_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr,
ReportHibernationEvent(
Canvas2DLayerBridge::kHibernationAbortedDueGpuContextLoss))
.WillOnce(testing::InvokeWithoutArgs(hibernation_aborted_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_aborted_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
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, test_thread.get(), &bridge);
}
#if CANVAS2D_HIBERNATION_ENABLED
TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxWhileHibernating)
#else
TEST_F(Canvas2DLayerBridgeTest, DISABLED_PrepareMailboxWhileHibernating)
#endif
{
FakeGLES2Interface gl;
std::unique_ptr<WebThread> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_started_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr, DidStartHibernating())
.WillOnce(testing::Invoke(hibernation_started_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_started_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
// Test prepareMailbox while hibernating
cc::TextureMailbox texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
EXPECT_FALSE(
bridge->PrepareTextureMailbox(&texture_mailbox, &release_callback));
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(*mock_logger_ptr,
ReportHibernationEvent(
Canvas2DLayerBridge::kHibernationEndedWithTeardown));
PostAndWaitDestroyBridgeTask(BLINK_FROM_HERE, test_thread.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> test_thread =
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, test_thread.get(), &bridge, &gl,
this);
// Register an alternate Logger for tracking hibernation events
std::unique_ptr<MockLogger> mock_logger = WTF::WrapUnique(new MockLogger);
MockLogger* mock_logger_ptr = mock_logger.get();
bridge->SetLoggerForTesting(std::move(mock_logger));
// Test entering hibernation
std::unique_ptr<WaitableEvent> hibernation_started_event =
WTF::MakeUnique<WaitableEvent>();
EXPECT_CALL(
*mock_logger_ptr,
ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
EXPECT_CALL(*mock_logger_ptr, DidStartHibernating())
.WillOnce(testing::Invoke(hibernation_started_event.get(),
&WaitableEvent::Signal));
PostSetIsHiddenTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get(), true);
hibernation_started_event->Wait();
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
// Rendering in the background -> temp switch to SW
EXPECT_CALL(*mock_logger_ptr,
ReportHibernationEvent(
Canvas2DLayerBridge::
kHibernationEndedWithSwitchToBackgroundRendering));
PostAndWaitRenderingTask(BLINK_FROM_HERE, test_thread.get(), bridge.Get());
::testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_FALSE(bridge->IsHibernating());
EXPECT_TRUE(bridge->CheckSurfaceValid());
// Test prepareMailbox while background rendering
cc::TextureMailbox texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
EXPECT_FALSE(
bridge->PrepareTextureMailbox(&texture_mailbox, &release_callback));
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, test_thread.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> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
cc::TextureMailbox texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
{
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kForceAccelerationForTesting,
CanvasColorParams())));
bridge->PrepareTextureMailbox(&texture_mailbox, &release_callback);
}
bool lost_resource = false;
release_callback->Run(gpu::SyncToken(), lost_resource);
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> context_provider =
WTF::WrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
EXPECT_CALL(gl, Flush()).Times(0);
Canvas2DLayerBridgePtr bridge(AdoptRef(new Canvas2DLayerBridge(
std::move(context_provider), IntSize(300, 150), 0, kNonOpaque,
Canvas2DLayerBridge::kForceAccelerationForTesting, CanvasColorParams())));
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