blob: 63d586c318dc14648d7f0848e30b7c1f2c3cf46a [file] [log] [blame]
// Copyright 2017 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 "platform/graphics/gpu/ImageLayerBridge.h"
#include "components/viz/common/quads/shared_bitmap.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "platform/graphics/AcceleratedStaticBitmapImage.h"
#include "platform/graphics/ColorBehavior.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/graphics/gpu/SharedGpuContext.h"
#include "public/platform/Platform.h"
#include "public/platform/WebCompositorSupport.h"
#include "public/platform/WebExternalTextureLayer.h"
#include "public/platform/WebGraphicsContext3DProvider.h"
namespace blink {
ImageLayerBridge::ImageLayerBridge(OpacityMode opacity_mode)
: opacity_mode_(opacity_mode) {
layer_ = Platform::Current()->CompositorSupport()->CreateExternalTextureLayer(
this);
GraphicsLayer::RegisterContentsLayer(layer_->Layer());
layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
if (opacity_mode_ == kOpaque) {
layer_->SetOpaque(true);
layer_->SetBlendBackgroundColor(false);
}
}
ImageLayerBridge::~ImageLayerBridge() {
if (!disposed_)
Dispose();
}
void ImageLayerBridge::SetImage(scoped_refptr<StaticBitmapImage> image) {
image_ = std::move(image);
if (image_) {
if (opacity_mode_ == kNonOpaque) {
layer_->SetOpaque(image_->CurrentFrameKnownToBeOpaque());
layer_->SetBlendBackgroundColor(!image_->CurrentFrameKnownToBeOpaque());
}
}
if (!has_presented_since_last_set_image_ && image_ &&
image_->IsTextureBacked()) {
// If the layer bridge is not presenting, the GrContext may not be getting
// flushed regularly. The flush is normally triggered inside the
// m_image->EnsureMailbox() call of
// ImageLayerBridge::PrepareTransferableResource. To prevent a potential
// memory leak we must flush the GrContext here.
image_->PaintImageForCurrentFrame().GetSkImage()->getTextureHandle(
true); // GrContext flush.
}
has_presented_since_last_set_image_ = false;
}
void ImageLayerBridge::Dispose() {
if (layer_) {
GraphicsLayer::UnregisterContentsLayer(layer_->Layer());
layer_->ClearTexture();
layer_.reset();
}
image_ = nullptr;
disposed_ = true;
}
bool ImageLayerBridge::PrepareTransferableResource(
viz::TransferableResource* out_resource,
std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
if (disposed_)
return false;
if (!image_)
return false;
if (has_presented_since_last_set_image_)
return false;
has_presented_since_last_set_image_ = true;
bool gpu_compositing = SharedGpuContext::IsGpuCompositingEnabled();
bool gpu_image = image_->IsTextureBacked();
// Expect software images for software compositing.
if (!gpu_compositing && gpu_image)
return false;
// If the texture comes from a software image then it does not need to be
// flipped.
layer_->SetFlipped(gpu_image);
scoped_refptr<StaticBitmapImage> image_for_compositor;
// Upload to a texture if the compositor is expecting one.
if (gpu_compositing && !image_->IsTextureBacked()) {
image_for_compositor =
image_->MakeAccelerated(SharedGpuContext::ContextProviderWrapper());
} else if (!gpu_compositing && image_->IsTextureBacked()) {
image_for_compositor = image_->MakeUnaccelerated();
} else {
image_for_compositor = image_;
}
DCHECK_EQ(image_for_compositor->IsTextureBacked(), gpu_compositing);
if (gpu_compositing) {
uint32_t filter =
filter_quality_ == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR;
image_for_compositor->EnsureMailbox(kUnverifiedSyncToken, filter);
*out_resource = viz::TransferableResource::MakeGL(
image_for_compositor->GetMailbox(), filter, GL_TEXTURE_2D,
image_for_compositor->GetSyncToken());
auto func =
WTF::Bind(&ImageLayerBridge::ResourceReleasedGpu,
WrapWeakPersistent(this), std::move(image_for_compositor));
*out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
} else {
std::unique_ptr<viz::SharedBitmap> bitmap =
CreateOrRecycleBitmap(image_for_compositor->Size());
if (!bitmap)
return false;
sk_sp<SkImage> sk_image =
image_for_compositor->PaintImageForCurrentFrame().GetSkImage();
if (!sk_image)
return false;
SkImageInfo dst_info =
SkImageInfo::MakeN32Premul(image_for_compositor->width(), 1);
size_t row_bytes = image_for_compositor->width() * 4;
// Copy from SkImage into |bitmap|, while flipping the Y axis.
for (int row = 0; row < image_for_compositor->height(); row++) {
if (!sk_image->readPixels(dst_info, bitmap->pixels(), row_bytes, 0, 0))
return false;
}
*out_resource = viz::TransferableResource::MakeSoftware(
bitmap->id(), bitmap->sequence_number(),
gfx::Size(image_for_compositor->width(),
image_for_compositor->height()));
auto func = WTF::Bind(&ImageLayerBridge::ResourceReleasedSoftware,
WrapWeakPersistent(this), base::Passed(&bitmap),
image_for_compositor->Size());
*out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
}
// TODO(junov): Figure out how to get the color space info.
// out_resource->color_space = ...;
return true;
}
std::unique_ptr<viz::SharedBitmap> ImageLayerBridge::CreateOrRecycleBitmap(
const IntSize& size) {
auto it = std::remove_if(
recycled_bitmaps_.begin(), recycled_bitmaps_.end(),
[&size](const RecycledBitmap& bitmap) { return bitmap.size != size; });
recycled_bitmaps_.Shrink(it - recycled_bitmaps_.begin());
if (!recycled_bitmaps_.IsEmpty()) {
RecycledBitmap recycled = std::move(recycled_bitmaps_.back());
recycled_bitmaps_.pop_back();
DCHECK(recycled.size == size);
return std::move(recycled.bitmap);
}
return Platform::Current()->AllocateSharedBitmap(size);
}
void ImageLayerBridge::ResourceReleasedGpu(
scoped_refptr<StaticBitmapImage> image,
const gpu::SyncToken& token,
bool lost_resource) {
if (image && image->IsValid()) {
DCHECK(image->IsTextureBacked());
if (token.HasData() && image->ContextProvider() &&
image->ContextProvider()->ContextGL()) {
image->ContextProvider()->ContextGL()->WaitSyncTokenCHROMIUM(
token.GetConstData());
}
}
// let 'image' go out of scope to release gpu resources.
}
void ImageLayerBridge::ResourceReleasedSoftware(
std::unique_ptr<viz::SharedBitmap> bitmap,
const IntSize& size,
const gpu::SyncToken& sync_token,
bool lost_resource) {
DCHECK(!sync_token.HasData()); // No sync tokens for software resources.
if (!disposed_ && !lost_resource) {
RecycledBitmap recycled = {std::move(bitmap), size};
recycled_bitmaps_.push_back(std::move(recycled));
}
}
WebLayer* ImageLayerBridge::PlatformLayer() const {
return layer_->Layer();
}
} // namespace blink