blob: 2e8a9c1d269c14841dad2ed9f0c321856c9722a0 [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/raster_interface.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/capabilities.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/mailbox_ref.h"
#include "third_party/blink/renderer/platform/graphics/mailbox_texture_backing.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/skia/include/core/SkImage.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
namespace blink {
// 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* ri = release_ctx->context_provider_wrapper->ContextProvider()
->RasterInterface();
ri->EndSharedImageAccessDirectCHROMIUM(release_ctx->texture_id);
ri->DeleteGpuRasterTexture(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, ImageOrientationEnum::kDefault,
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)
InitializeTextureBacking(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(
PaintImageForCurrentFrame().GetSwSkImage(), 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_->sync_token().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_texture_backing(texture_backing_, 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,
const SkSamplingOptions& sampling,
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, sampling,
image_clamping_mode,
should_respect_image_orientation, paint_image);
}
bool AcceleratedStaticBitmapImage::IsValid() const {
if (texture_backing_ && !skia_context_provider_wrapper_)
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 texture_backing_ ? skia_context_provider_wrapper_
: context_provider_wrapper_;
}
void AcceleratedStaticBitmapImage::CreateImageFromMailboxIfNeeded() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (texture_backing_)
return;
InitializeTextureBacking(0u);
}
void AcceleratedStaticBitmapImage::InitializeTextureBacking(
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::raster::RasterInterface* shared_ri =
context_provider_wrapper->ContextProvider()->RasterInterface();
shared_ri->WaitSyncTokenCHROMIUM(mailbox_ref_->sync_token().GetConstData());
if (context_provider_wrapper->ContextProvider()
->GetCapabilities()
.supports_oop_raster) {
DCHECK_EQ(shared_image_texture_id, 0u);
skia_context_provider_wrapper_ = context_provider_wrapper;
texture_backing_ = sk_make_sp<MailboxTextureBacking>(
mailbox_, mailbox_ref_, sk_image_info_,
std::move(context_provider_wrapper));
return;
}
GrDirectContext* shared_gr_context =
context_provider_wrapper->ContextProvider()->GetGrContext();
DCHECK(shared_ri &&
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_context_texture_id =
shared_ri->CreateAndConsumeForGpuRaster(mailbox_);
shared_ri->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 =
CanvasResourceParams(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_sp<SkImage> 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) {
skia_context_provider_wrapper_ = context_provider_wrapper;
texture_backing_ = sk_make_sp<MailboxTextureBacking>(
std::move(sk_image), mailbox_ref_, sk_image_info_,
std::move(context_provider_wrapper));
}
}
void AcceleratedStaticBitmapImage::EnsureSyncTokenVerified() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (mailbox_ref_->verified_flush())
return;
// If the original context was created on a different thread, we need to
// fallback to using the shared GPU context.
auto context_provider_wrapper =
mailbox_ref_->is_cross_thread()
? SharedGpuContext::ContextProviderWrapper()
: ContextProviderWrapper();
if (!context_provider_wrapper)
return;
auto sync_token = mailbox_ref_->sync_token();
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_->sync_token(),
texture_target_);
}
void AcceleratedStaticBitmapImage::Transfer() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// SkImage is bound to the current thread so is no longer valid to use
// cross-thread.
texture_backing_.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;
SkImageInfo image_info = PaintImageForCurrentFrame().GetSkImageInfo();
if (SkColorSpace::Equals(color_space.get(), image_info.colorSpace()) &&
color_type == image_info.colorType()) {
return this;
}
image_info = image_info.makeColorSpace(color_space).makeColorType(color_type);
auto usage_flags = ContextProviderWrapper()
->ContextProvider()
->SharedImageInterface()
->UsageForMailbox(mailbox_);
auto provider = CanvasResourceProvider::CreateSharedImageProvider(
Size(), kLow_SkFilterQuality, CanvasResourceParams(image_info),
CanvasResourceProvider::ShouldInitialize::kNo, ContextProviderWrapper(),
RasterMode::kGPU, IsOriginTopLeft(), usage_flags);
if (!provider) {
return nullptr;
}
cc::PaintFlags paint;
paint.setBlendMode(SkBlendMode::kSrc);
provider->Canvas()->drawImage(PaintImageForCurrentFrame(), 0, 0,
SkSamplingOptions(), &paint);
return provider->Snapshot(orientation_);
}
} // namespace blink