blob: 0038f56efb813ce4012d6ec5d55f3b1c72a44534 [file] [log] [blame]
// Copyright 2016 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/accelerated_static_bitmap_image.h"
#include <memory>
#include <utility>
#include "components/viz/common/resources/single_release_callback.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
#include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/skia/include/core/SkImage.h"
namespace blink {
namespace {
void ReleaseCallbackOnContextThread(
std::unique_ptr<viz::SingleReleaseCallback> callback,
const gpu::SyncToken sync_token) {
callback->Run(sync_token, /* is_lost = */ false);
}
} // namespace
AcceleratedStaticBitmapImage::MailboxRef::MailboxRef(
const gpu::SyncToken& sync_token,
base::PlatformThreadRef context_thread_ref,
scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
std::unique_ptr<viz::SingleReleaseCallback> release_callback)
: sync_token_(sync_token),
context_thread_ref_(context_thread_ref),
context_task_runner_(std::move(context_task_runner)),
release_callback_(std::move(release_callback)) {
DCHECK(!is_cross_thread() || sync_token_.verified_flush());
}
AcceleratedStaticBitmapImage::MailboxRef::~MailboxRef() {
if (context_thread_ref_ == base::PlatformThread::CurrentRef()) {
ReleaseCallbackOnContextThread(std::move(release_callback_), sync_token_);
} else {
context_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&ReleaseCallbackOnContextThread,
std::move(release_callback_), sync_token_));
}
}
const gpu::SyncToken&
AcceleratedStaticBitmapImage::MailboxRef::GetOrCreateSyncToken(
base::WeakPtr<WebGraphicsContext3DProviderWrapper>
context_provider_wrapper) {
if (!sync_token_.HasData()) {
DCHECK(!is_cross_thread());
DCHECK(context_provider_wrapper);
context_provider_wrapper->ContextProvider()
->InterfaceBase()
->GenUnverifiedSyncTokenCHROMIUM(sync_token_.GetData());
}
return sync_token_;
}
// static
void AcceleratedStaticBitmapImage::ReleaseTexture(void* ctx) {
auto* release_ctx = static_cast<ReleaseContext*>(ctx);
if (release_ctx->context_provider_wrapper) {
if (release_ctx->texture_id) {
auto* gl =
release_ctx->context_provider_wrapper->ContextProvider()->ContextGL();
gl->EndSharedImageAccessDirectCHROMIUM(release_ctx->texture_id);
gl->DeleteTextures(1u, &release_ctx->texture_id);
}
}
delete release_ctx;
}
// static
scoped_refptr<AcceleratedStaticBitmapImage>
AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
const gpu::Mailbox& mailbox,
const gpu::SyncToken& sync_token,
GLuint shared_image_texture_id,
const SkImageInfo& sk_image_info,
GLenum texture_target,
bool is_origin_top_left,
base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
base::PlatformThreadRef context_thread_ref,
scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
std::unique_ptr<viz::SingleReleaseCallback> release_callback) {
return base::AdoptRef(new AcceleratedStaticBitmapImage(
mailbox, sync_token, shared_image_texture_id, sk_image_info,
texture_target, is_origin_top_left, kDefaultImageOrientation,
std::move(context_provider_wrapper), context_thread_ref,
std::move(context_task_runner), std::move(release_callback)));
}
AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage(
const gpu::Mailbox& mailbox,
const gpu::SyncToken& sync_token,
GLuint shared_image_texture_id,
const SkImageInfo& sk_image_info,
GLenum texture_target,
bool is_origin_top_left,
const ImageOrientation& orientation,
base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
base::PlatformThreadRef context_thread_ref,
scoped_refptr<base::SingleThreadTaskRunner> context_task_runner,
std::unique_ptr<viz::SingleReleaseCallback> release_callback)
: StaticBitmapImage(orientation),
mailbox_(mailbox),
sk_image_info_(sk_image_info),
texture_target_(texture_target),
is_origin_top_left_(is_origin_top_left),
context_provider_wrapper_(std::move(context_provider_wrapper)),
mailbox_ref_(
base::MakeRefCounted<MailboxRef>(sync_token,
context_thread_ref,
std::move(context_task_runner),
std::move(release_callback))),
paint_image_content_id_(cc::PaintImage::GetNextContentId()) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(mailbox_.IsSharedImage());
if (shared_image_texture_id)
InitializeSkImage(shared_image_texture_id);
}
AcceleratedStaticBitmapImage::~AcceleratedStaticBitmapImage() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
IntSize AcceleratedStaticBitmapImage::Size() const {
return IntSize(sk_image_info_.width(), sk_image_info_.height());
}
scoped_refptr<StaticBitmapImage>
AcceleratedStaticBitmapImage::MakeUnaccelerated() {
CreateImageFromMailboxIfNeeded();
return UnacceleratedStaticBitmapImage::Create(
sk_image_->makeNonTextureImage(), orientation_);
}
bool AcceleratedStaticBitmapImage::CopyToTexture(
gpu::gles2::GLES2Interface* dest_gl,
GLenum dest_target,
GLuint dest_texture_id,
GLint dest_level,
bool unpack_premultiply_alpha,
bool unpack_flip_y,
const IntPoint& dest_point,
const IntRect& source_sub_rectangle) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!IsValid())
return false;
// This method should only be used for cross-context copying, otherwise it's
// wasting overhead.
DCHECK(mailbox_ref_->is_cross_thread() ||
dest_gl != ContextProvider()->ContextGL());
DCHECK(mailbox_.IsSharedImage());
// Get a texture id that |destProvider| knows about and copy from it.
dest_gl->WaitSyncTokenCHROMIUM(
mailbox_ref_->GetOrCreateSyncToken(ContextProviderWrapper())
.GetConstData());
GLuint source_texture_id =
dest_gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox_.name);
dest_gl->BeginSharedImageAccessDirectCHROMIUM(
source_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
dest_gl->CopySubTextureCHROMIUM(
source_texture_id, 0, dest_target, dest_texture_id, dest_level,
dest_point.X(), dest_point.Y(), source_sub_rectangle.X(),
source_sub_rectangle.Y(), source_sub_rectangle.Width(),
source_sub_rectangle.Height(), unpack_flip_y ? GL_FALSE : GL_TRUE,
GL_FALSE, unpack_premultiply_alpha ? GL_FALSE : GL_TRUE);
dest_gl->EndSharedImageAccessDirectCHROMIUM(source_texture_id);
dest_gl->DeleteTextures(1, &source_texture_id);
// We need to update the texture holder's sync token to ensure that when this
// mailbox is recycled or deleted, it is done after the copy operation above.
gpu::SyncToken sync_token;
dest_gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
mailbox_ref_->set_sync_token(sync_token);
return true;
}
PaintImage AcceleratedStaticBitmapImage::PaintImageForCurrentFrame() {
// TODO(ccameron): This function should not ignore |colorBehavior|.
// https://crbug.com/672306
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!IsValid())
return PaintImage();
CreateImageFromMailboxIfNeeded();
return CreatePaintImageBuilder()
.set_image(sk_image_, paint_image_content_id_)
.set_completion_state(PaintImage::CompletionState::DONE)
.TakePaintImage();
}
void AcceleratedStaticBitmapImage::Draw(
cc::PaintCanvas* canvas,
const cc::PaintFlags& flags,
const FloatRect& dst_rect,
const FloatRect& src_rect,
RespectImageOrientationEnum should_respect_image_orientation,
ImageClampingMode image_clamping_mode,
ImageDecodingMode decode_mode) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto paint_image = PaintImageForCurrentFrame();
if (!paint_image)
return;
auto paint_image_decoding_mode = ToPaintImageDecodingMode(decode_mode);
if (paint_image.decoding_mode() != paint_image_decoding_mode) {
paint_image = PaintImageBuilder::WithCopy(std::move(paint_image))
.set_decoding_mode(paint_image_decoding_mode)
.TakePaintImage();
}
StaticBitmapImage::DrawHelper(canvas, flags, dst_rect, src_rect,
image_clamping_mode,
should_respect_image_orientation, paint_image);
}
bool AcceleratedStaticBitmapImage::IsValid() const {
if (sk_image_ && (!skia_context_provider_wrapper_ ||
!sk_image_->isValid(ContextProvider()->GetGrContext()))) {
return false;
}
if (mailbox_ref_->is_cross_thread()) {
// If context is is from another thread, validity cannot be verified. Just
// assume valid. Potential problem will be detected later.
return true;
}
return !!context_provider_wrapper_;
}
WebGraphicsContext3DProvider* AcceleratedStaticBitmapImage::ContextProvider()
const {
auto context = ContextProviderWrapper();
return context ? context->ContextProvider() : nullptr;
}
base::WeakPtr<WebGraphicsContext3DProviderWrapper>
AcceleratedStaticBitmapImage::ContextProviderWrapper() const {
return sk_image_ ? skia_context_provider_wrapper_ : context_provider_wrapper_;
}
void AcceleratedStaticBitmapImage::CreateImageFromMailboxIfNeeded() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (sk_image_)
return;
InitializeSkImage(0u);
}
void AcceleratedStaticBitmapImage::InitializeSkImage(
GLuint shared_image_texture_id) {
DCHECK(!shared_image_texture_id || !mailbox_ref_->is_cross_thread());
auto context_provider_wrapper = SharedGpuContext::ContextProviderWrapper();
if (!context_provider_wrapper)
return;
gpu::gles2::GLES2Interface* shared_gl =
context_provider_wrapper->ContextProvider()->ContextGL();
GrContext* shared_gr_context =
context_provider_wrapper->ContextProvider()->GetGrContext();
DCHECK(shared_gl &&
shared_gr_context); // context isValid already checked in callers
GLuint shared_context_texture_id = 0u;
bool should_delete_texture_on_release = true;
if (shared_image_texture_id) {
shared_context_texture_id = shared_image_texture_id;
should_delete_texture_on_release = false;
} else {
shared_gl->WaitSyncTokenCHROMIUM(
mailbox_ref_->GetOrCreateSyncToken(context_provider_wrapper)
.GetConstData());
shared_context_texture_id =
shared_gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox_.name);
shared_gl->BeginSharedImageAccessDirectCHROMIUM(
shared_context_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
}
GrGLTextureInfo texture_info;
texture_info.fTarget = texture_target_;
texture_info.fID = shared_context_texture_id;
texture_info.fFormat =
CanvasColorParams(sk_image_info_).GLSizedInternalFormat();
GrBackendTexture backend_texture(sk_image_info_.width(),
sk_image_info_.height(), GrMipMapped::kNo,
texture_info);
GrSurfaceOrigin origin = IsOriginTopLeft() ? kTopLeft_GrSurfaceOrigin
: kBottomLeft_GrSurfaceOrigin;
auto* release_ctx = new ReleaseContext;
release_ctx->mailbox_ref = mailbox_ref_;
if (should_delete_texture_on_release)
release_ctx->texture_id = shared_context_texture_id;
release_ctx->context_provider_wrapper = context_provider_wrapper;
sk_image_ = SkImage::MakeFromTexture(
shared_gr_context, backend_texture, origin, sk_image_info_.colorType(),
sk_image_info_.alphaType(), sk_image_info_.refColorSpace(),
&ReleaseTexture, release_ctx);
if (!sk_image_)
ReleaseTexture(release_ctx);
else
skia_context_provider_wrapper_ = std::move(context_provider_wrapper);
}
void AcceleratedStaticBitmapImage::EnsureSyncTokenVerified() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (mailbox_ref_->verified_flush())
return;
if (mailbox_ref_->is_cross_thread()) {
// Was originally created on another thread. Should already have a sync
// token from the original source context, already verified if needed.
NOTREACHED() << "Cross-thread SyncToken should already be verified.";
return;
}
if (!ContextProviderWrapper())
return;
auto sync_token =
mailbox_ref_->GetOrCreateSyncToken(ContextProviderWrapper());
int8_t* token_data = sync_token.GetData();
ContextProvider()->InterfaceBase()->VerifySyncTokensCHROMIUM(&token_data, 1);
sync_token.SetVerifyFlush();
mailbox_ref_->set_sync_token(sync_token);
}
gpu::MailboxHolder AcceleratedStaticBitmapImage::GetMailboxHolder() const {
if (!IsValid())
return gpu::MailboxHolder();
return gpu::MailboxHolder(
mailbox_, mailbox_ref_->GetOrCreateSyncToken(ContextProviderWrapper()),
texture_target_);
}
void AcceleratedStaticBitmapImage::Transfer() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
EnsureSyncTokenVerified();
// SkImage is bound to the current thread so is no longer valid to use
// cross-thread.
sk_image_.reset();
DETACH_FROM_THREAD(thread_checker_);
}
bool AcceleratedStaticBitmapImage::CurrentFrameKnownToBeOpaque() {
return sk_image_info_.isOpaque();
}
scoped_refptr<StaticBitmapImage>
AcceleratedStaticBitmapImage::ConvertToColorSpace(
sk_sp<SkColorSpace> color_space,
SkColorType color_type) {
DCHECK(color_space);
DCHECK(color_type == kRGBA_F16_SkColorType ||
color_type == kRGBA_8888_SkColorType);
if (!ContextProviderWrapper())
return nullptr;
sk_sp<SkImage> skia_image = PaintImageForCurrentFrame().GetSkImage();
if (SkColorSpace::Equals(color_space.get(), skia_image->colorSpace()) &&
color_type == skia_image->colorType()) {
return this;
}
auto image_info = skia_image->imageInfo()
.makeColorSpace(color_space)
.makeColorType(color_type);
auto usage_flags = ContextProviderWrapper()
->ContextProvider()
->SharedImageInterface()
->UsageForMailbox(mailbox_);
auto provider = CanvasResourceProvider::CreateSharedImageProvider(
Size(), ContextProviderWrapper(), kLow_SkFilterQuality,
CanvasColorParams(image_info), IsOriginTopLeft(),
CanvasResourceProvider::RasterMode::kGPU, usage_flags);
if (!provider) {
return nullptr;
}
provider->Canvas()->drawImage(PaintImageForCurrentFrame(), 0, 0, nullptr);
return provider->Snapshot(orientation_);
}
} // namespace blink