blob: 0ca9fb3cb2e88508cede00ed41c906735d9df6f9 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
#include "components/viz/common/resources/single_release_callback.h"
#include "components/viz/test/test_gles2_interface.h"
#include "components/viz/test/test_gpu_memory_buffer_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
#include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
#include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
#include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
#include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
using testing::_;
using testing::InSequence;
using testing::Return;
using testing::Test;
namespace blink {
namespace {
class MockCanvasResourceDispatcherClient
: public CanvasResourceDispatcherClient {
public:
MockCanvasResourceDispatcherClient() = default;
MOCK_METHOD0(BeginFrame, void());
};
class MockWebGraphisContext3DProviderWrapper
: public WebGraphicsContext3DProvider {
public:
MockWebGraphisContext3DProviderWrapper(cc::ImageDecodeCache* cache = nullptr)
: image_decode_cache_(cache ? cache : &stub_image_decode_cache_) {
// enable all gpu features.
for (unsigned feature = 0; feature < gpu::NUMBER_OF_GPU_FEATURE_TYPES;
++feature) {
gpu_feature_info_.status_values[feature] = gpu::kGpuFeatureStatusEnabled;
}
}
~MockWebGraphisContext3DProviderWrapper() override = default;
GrContext* GetGrContext() override {
return GetTestContextProvider()->GrContext();
}
const gpu::Capabilities& GetCapabilities() const override {
return capabilities_;
}
void SetCapabilities(const gpu::Capabilities& c) { capabilities_ = c; }
const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override {
return gpu_feature_info_;
}
const WebglPreferences& GetWebglPreferences() const override {
return webgl_preferences_;
}
viz::GLHelper* GetGLHelper() override { return nullptr; }
gpu::gles2::GLES2Interface* ContextGL() override {
return GetTestContextProvider()->ContextGL();
}
gpu::webgpu::WebGPUInterface* WebGPUInterface() override { return nullptr; }
scoped_refptr<viz::TestContextProvider> GetTestContextProvider() {
if (!test_context_provider_) {
test_context_provider_ = viz::TestContextProvider::Create();
// Needed for CanvasResourceProviderDirect2DGpuMemoryBuffer.
test_context_provider_->UnboundTestContextGL()
->set_support_texture_format_bgra8888(true);
test_context_provider_->BindToCurrentThread();
}
return test_context_provider_;
}
bool BindToCurrentThread() override { return false; }
void SetLostContextCallback(base::RepeatingClosure) override {}
void SetErrorMessageCallback(
base::RepeatingCallback<void(const char*, int32_t id)>) override {}
cc::ImageDecodeCache* ImageDecodeCache(SkColorType color_type) override {
return image_decode_cache_;
}
viz::TestSharedImageInterface* SharedImageInterface() override {
return GetTestContextProvider()->SharedImageInterface();
}
private:
cc::StubDecodeCache stub_image_decode_cache_;
scoped_refptr<viz::TestContextProvider> test_context_provider_;
gpu::Capabilities capabilities_;
gpu::GpuFeatureInfo gpu_feature_info_;
WebglPreferences webgl_preferences_;
cc::ImageDecodeCache* image_decode_cache_;
};
} // anonymous namespace
class CanvasResourceProviderTest : public Test {
public:
void SetUp() override {
// Install our mock GL context so that it gets served by SharedGpuContext.
auto factory = [](bool* gpu_compositing_disabled)
-> std::unique_ptr<WebGraphicsContext3DProvider> {
*gpu_compositing_disabled = false;
// Unretained is safe since TearDown() cleans up the SharedGpuContext.
return std::make_unique<MockWebGraphisContext3DProviderWrapper>();
};
SharedGpuContext::SetContextProviderFactoryForTesting(
WTF::BindRepeating(factory));
context_provider_wrapper_ = SharedGpuContext::ContextProviderWrapper();
}
void TearDown() override { SharedGpuContext::ResetForTesting(); }
// Adds |buffer_format| to the context capabilities if it's not supported.
void EnsureBufferFormatIsSupported(gfx::BufferFormat buffer_format) {
auto* context_provider = context_provider_wrapper_->ContextProvider();
if (context_provider->GetCapabilities().gpu_memory_buffer_formats.Has(
buffer_format)) {
return;
}
auto capabilities = context_provider->GetCapabilities();
capabilities.gpu_memory_buffer_formats.Add(buffer_format);
static_cast<MockWebGraphisContext3DProviderWrapper*>(context_provider)
->SetCapabilities(capabilities);
}
protected:
base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform_;
};
TEST_F(CanvasResourceProviderTest,
CanvasResourceProviderTextureGpuMemoryBuffer) {
const IntSize kSize(10, 10);
const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
kRGBA8CanvasPixelFormat, kNonOpaque);
EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
auto provider = CanvasResourceProvider::CreateForTesting(
kSize, CanvasResourceProvider::kTextureGpuMemoryBuffer,
context_provider_wrapper_, 0 /* msaa_sample_count */, kColorParams,
CanvasResourceProvider::kAllowImageChromiumPresentationMode,
nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
EXPECT_TRUE(provider->IsAccelerated());
EXPECT_TRUE(provider->SupportsDirectCompositing());
EXPECT_TRUE(provider->SupportsSingleBuffering());
EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
kColorParams.GetOpacityMode());
EXPECT_FALSE(provider->IsSingleBuffered());
provider->TryEnableSingleBuffering();
EXPECT_TRUE(provider->IsSingleBuffered());
}
TEST_F(CanvasResourceProviderTest, CanvasResourceProviderTexture) {
const IntSize kSize(10, 10);
const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
kRGBA8CanvasPixelFormat, kNonOpaque);
auto provider = CanvasResourceProvider::Create(
kSize, CanvasResourceProvider::kAcceleratedResourceUsage,
context_provider_wrapper_, 0 /* msaa_sample_count */, kColorParams,
CanvasResourceProvider::kAllowImageChromiumPresentationMode,
nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
EXPECT_TRUE(provider->IsAccelerated());
EXPECT_TRUE(provider->SupportsDirectCompositing());
EXPECT_FALSE(provider->SupportsSingleBuffering());
EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
kColorParams.GetOpacityMode());
EXPECT_FALSE(provider->IsSingleBuffered());
}
TEST_F(CanvasResourceProviderTest,
CanvasResourceProviderBitmapGpuMemoryBuffer) {
const IntSize kSize(10, 10);
const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
kRGBA8CanvasPixelFormat, kNonOpaque);
EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
auto provider = CanvasResourceProvider::Create(
kSize, CanvasResourceProvider::kSoftwareCompositedResourceUsage,
context_provider_wrapper_, 0 /* msaa_sample_count */, kColorParams,
CanvasResourceProvider::kAllowImageChromiumPresentationMode,
nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
EXPECT_FALSE(provider->IsAccelerated());
EXPECT_TRUE(provider->SupportsDirectCompositing());
EXPECT_TRUE(provider->SupportsSingleBuffering());
EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
kColorParams.GetOpacityMode());
EXPECT_FALSE(provider->IsSingleBuffered());
}
TEST_F(CanvasResourceProviderTest, CanvasResourceProviderSharedImage) {
const IntSize kSize(10, 10);
const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
kRGBA8CanvasPixelFormat, kNonOpaque);
EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
auto provider = CanvasResourceProvider::CreateForTesting(
kSize, CanvasResourceProvider::kSharedImage, context_provider_wrapper_,
0 /* msaa_sample_count */, kColorParams,
CanvasResourceProvider::kAllowImageChromiumPresentationMode,
nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
EXPECT_TRUE(provider->IsAccelerated());
EXPECT_FALSE(provider->IsSingleBuffered());
EXPECT_FALSE(provider->SupportsSingleBuffering());
EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
kColorParams.GetOpacityMode());
// Same resource and sync token if we query again without updating.
auto resource = provider->ProduceCanvasResource();
auto sync_token = resource->GetSyncToken();
ASSERT_TRUE(resource);
EXPECT_EQ(resource, provider->ProduceCanvasResource());
EXPECT_EQ(sync_token, resource->GetSyncToken());
// Resource updated after draw.
provider->Canvas()->clear(SK_ColorWHITE);
auto new_resource = provider->ProduceCanvasResource();
EXPECT_NE(resource, new_resource);
EXPECT_NE(sync_token, new_resource->GetSyncToken());
// Resource recycled.
viz::TransferableResource transferable_resource;
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
ASSERT_TRUE(resource->PrepareTransferableResource(
&transferable_resource, &release_callback, kUnverifiedSyncToken));
auto* resource_ptr = resource.get();
resource = nullptr;
release_callback->Run(sync_token, false);
provider->Canvas()->clear(SK_ColorBLACK);
auto resource_again = provider->ProduceCanvasResource();
EXPECT_EQ(resource_ptr, resource_again);
EXPECT_NE(sync_token, resource_again->GetSyncToken());
}
TEST_F(CanvasResourceProviderTest,
CanvasResourceProviderSharedImageStaticBitmapImage) {
const IntSize kSize(10, 10);
const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
kRGBA8CanvasPixelFormat, kNonOpaque);
EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
auto provider = CanvasResourceProvider::CreateForTesting(
kSize, CanvasResourceProvider::kSharedImage, context_provider_wrapper_,
0 /* msaa_sample_count */, kColorParams,
CanvasResourceProvider::kAllowImageChromiumPresentationMode,
nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
ASSERT_TRUE(provider->IsValid());
// Same resource returned until the canvas is updated.
auto image = provider->Snapshot();
ASSERT_TRUE(image);
auto new_image = provider->Snapshot();
EXPECT_EQ(image->GetMailbox(), new_image->GetMailbox());
EXPECT_EQ(provider->ProduceCanvasResource()->GetOrCreateGpuMailbox(
kOrderingBarrier),
image->GetMailbox());
// Resource updated after draw.
provider->Canvas()->clear(SK_ColorWHITE);
new_image = provider->Snapshot();
EXPECT_NE(new_image->GetMailbox(), image->GetMailbox());
// Resource recycled.
auto original_mailbox = image->GetMailbox();
image.reset();
provider->Canvas()->clear(SK_ColorBLACK);
EXPECT_EQ(original_mailbox, provider->Snapshot()->GetMailbox());
}
TEST_F(CanvasResourceProviderTest,
CanvasResourceProviderSharedImageCopyOnWriteDisabled) {
auto* mock_context = static_cast<MockWebGraphisContext3DProviderWrapper*>(
context_provider_wrapper_->ContextProvider());
auto caps = mock_context->GetCapabilities();
caps.disable_2d_canvas_copy_on_write = true;
mock_context->SetCapabilities(caps);
const IntSize kSize(10, 10);
const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
kRGBA8CanvasPixelFormat, kNonOpaque);
EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
auto provider = CanvasResourceProvider::CreateForTesting(
kSize, CanvasResourceProvider::kSharedImage, context_provider_wrapper_,
0 /* msaa_sample_count */, kColorParams,
CanvasResourceProvider::kAllowImageChromiumPresentationMode,
nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
ASSERT_TRUE(provider->IsValid());
// Disabling copy-on-write forces a copy each time the resource is queried.
auto resource = provider->ProduceCanvasResource();
EXPECT_NE(resource->GetOrCreateGpuMailbox(kOrderingBarrier),
provider->ProduceCanvasResource()->GetOrCreateGpuMailbox(
kOrderingBarrier));
}
TEST_F(CanvasResourceProviderTest, CanvasResourceProviderBitmap) {
const IntSize kSize(10, 10);
const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
kRGBA8CanvasPixelFormat, kNonOpaque);
auto provider = CanvasResourceProvider::Create(
kSize, CanvasResourceProvider::kSoftwareResourceUsage,
context_provider_wrapper_, 0 /* msaa_sample_count */, kColorParams,
CanvasResourceProvider::kAllowImageChromiumPresentationMode,
nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
EXPECT_FALSE(provider->IsAccelerated());
EXPECT_FALSE(provider->SupportsDirectCompositing());
EXPECT_FALSE(provider->SupportsSingleBuffering());
EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
kColorParams.GetOpacityMode());
EXPECT_FALSE(provider->IsSingleBuffered());
}
TEST_F(CanvasResourceProviderTest, CanvasResourceProviderSharedBitmap) {
const IntSize kSize(10, 10);
const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
kRGBA8CanvasPixelFormat, kNonOpaque);
MockCanvasResourceDispatcherClient client;
CanvasResourceDispatcher resource_dispatcher(
&client, 1 /* client_id */, 1 /* sink_id */,
1 /* placeholder_canvas_id */, kSize);
auto provider = CanvasResourceProvider::Create(
kSize, CanvasResourceProvider::kSoftwareCompositedResourceUsage,
context_provider_wrapper_, 0 /* msaa_sample_count */, kColorParams,
CanvasResourceProvider::kDefaultPresentationMode,
resource_dispatcher.GetWeakPtr(), true /* is_origin_top_left */);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
EXPECT_FALSE(provider->IsAccelerated());
EXPECT_TRUE(provider->SupportsDirectCompositing());
EXPECT_TRUE(provider->SupportsSingleBuffering());
EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
kColorParams.GetOpacityMode());
EXPECT_FALSE(provider->IsSingleBuffered());
provider->TryEnableSingleBuffering();
EXPECT_TRUE(provider->IsSingleBuffered());
}
TEST_F(CanvasResourceProviderTest,
CanvasResourceProviderDirect2DGpuMemoryBuffer) {
const IntSize kSize(10, 10);
const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
kRGBA8CanvasPixelFormat, kNonOpaque);
EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
auto provider = CanvasResourceProvider::Create(
kSize, CanvasResourceProvider::kAcceleratedDirect2DResourceUsage,
context_provider_wrapper_, 0 /* msaa_sample_count */, kColorParams,
CanvasResourceProvider::kAllowImageChromiumPresentationMode,
nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
EXPECT_TRUE(provider->IsAccelerated());
EXPECT_TRUE(provider->SupportsDirectCompositing());
EXPECT_TRUE(provider->SupportsSingleBuffering());
EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
kColorParams.GetOpacityMode());
EXPECT_FALSE(provider->IsSingleBuffered());
provider->TryEnableSingleBuffering();
EXPECT_TRUE(provider->IsSingleBuffered());
}
TEST_F(CanvasResourceProviderTest,
CanvasResourceProviderDirect3DGpuMemoryBuffer) {
const IntSize kSize(10, 10);
const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
kRGBA8CanvasPixelFormat, kNonOpaque);
EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
auto provider = CanvasResourceProvider::Create(
kSize, CanvasResourceProvider::kAcceleratedDirect3DResourceUsage,
context_provider_wrapper_, 0 /* msaa_sample_count */, kColorParams,
CanvasResourceProvider::kAllowImageChromiumPresentationMode,
nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
EXPECT_TRUE(provider->IsAccelerated());
EXPECT_TRUE(provider->SupportsDirectCompositing());
EXPECT_TRUE(provider->SupportsSingleBuffering());
EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
kColorParams.GetOpacityMode());
EXPECT_FALSE(provider->IsSingleBuffered());
provider->TryEnableSingleBuffering();
EXPECT_TRUE(provider->IsSingleBuffered());
gpu::Mailbox mailbox = gpu::Mailbox::Generate();
scoped_refptr<ExternalCanvasResource> resource =
ExternalCanvasResource::Create(
mailbox, kSize, GL_TEXTURE_2D, kColorParams,
SharedGpuContext::ContextProviderWrapper(), provider->CreateWeakPtr(),
kNone_SkFilterQuality);
// NewOrRecycledResource() would return nullptr before an ImportResource().
EXPECT_TRUE(provider->ImportResource(resource));
EXPECT_EQ(provider->NewOrRecycledResource(), resource);
// NewOrRecycledResource() will always return the same |resource|.
EXPECT_EQ(provider->NewOrRecycledResource(), resource);
}
} // namespace blink