blob: 3fe805377b648b68e2049cc7ef5b40faf177bf6b [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/OffscreenCanvasResourceProvider.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "platform/graphics/gpu/SharedGpuContext.h"
#include "platform/wtf/typed_arrays/ArrayBuffer.h"
#include "platform/wtf/typed_arrays/Uint8Array.h"
#include "public/platform/Platform.h"
#include "public/platform/WebGraphicsContext3DProvider.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/gpu/GrContext.h"
namespace blink {
OffscreenCanvasResourceProvider::OffscreenCanvasResourceProvider(int width,
int height)
: width_(width), height_(height), next_resource_id_(0u) {}
OffscreenCanvasResourceProvider::~OffscreenCanvasResourceProvider() {}
std::unique_ptr<OffscreenCanvasResourceProvider::FrameResource>
OffscreenCanvasResourceProvider::CreateOrRecycleFrameResource() {
if (recyclable_resource_) {
recyclable_resource_->spare_lock_ = true;
return std::move(recyclable_resource_);
}
return std::unique_ptr<FrameResource>(new FrameResource());
}
void OffscreenCanvasResourceProvider::TransferResource(
viz::TransferableResource* resource) {
resource->id = next_resource_id_;
resource->format = viz::ResourceFormat::RGBA_8888;
resource->size = gfx::Size(width_, height_);
// This indicates the filtering on the resource inherently, not the desired
// filtering effect on the quad.
resource->filter = GL_NEAREST;
// TODO(crbug.com/646022): making this overlay-able.
resource->is_overlay_candidate = false;
}
void OffscreenCanvasResourceProvider::SetTransferableResourceToSharedBitmap(
viz::TransferableResource& resource,
RefPtr<StaticBitmapImage> image) {
std::unique_ptr<FrameResource> frame_resource =
CreateOrRecycleFrameResource();
if (!frame_resource->shared_bitmap_) {
frame_resource->shared_bitmap_ =
Platform::Current()->AllocateSharedBitmap(IntSize(width_, height_));
if (!frame_resource->shared_bitmap_)
return;
}
unsigned char* pixels = frame_resource->shared_bitmap_->pixels();
DCHECK(pixels);
SkImageInfo image_info = SkImageInfo::Make(
width_, height_, kN32_SkColorType,
image->IsPremultiplied() ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
// TODO(xlai): Optimize to avoid copying pixels. See crbug.com/651456.
// However, in the case when |image| is texture backed, this function call
// does a GPU readback which is required.
image->PaintImageForCurrentFrame().GetSkImage()->readPixels(
image_info, pixels, image_info.minRowBytes(), 0, 0);
resource.mailbox_holder.mailbox = frame_resource->shared_bitmap_->id();
resource.mailbox_holder.texture_target = 0;
resource.is_software = true;
resources_.insert(next_resource_id_, std::move(frame_resource));
}
void OffscreenCanvasResourceProvider::SetTransferableResourceToSharedGPUContext(
viz::TransferableResource& resource,
RefPtr<StaticBitmapImage> image) {
DCHECK(!image->IsTextureBacked());
// TODO(crbug.com/652707): When committing the first frame, there is no
// instance of SharedGpuContext yet, calling SharedGpuContext::gl() will
// trigger a creation of an instace, which requires to create a
// WebGraphicsContext3DProvider. This process is quite expensive, because
// WebGraphicsContext3DProvider can only be constructed on the main thread,
// and bind to the worker thread if commit() is called on worker. In the
// subsequent frame, we should already have a SharedGpuContext, then getting
// the gl interface should not be expensive.
WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
SharedGpuContext::ContextProviderWrapper();
if (!context_provider_wrapper)
return;
gpu::gles2::GLES2Interface* gl =
context_provider_wrapper->ContextProvider()->ContextGL();
GrContext* gr = context_provider_wrapper->ContextProvider()->GetGrContext();
if (!gl || !gr)
return;
std::unique_ptr<FrameResource> frame_resource =
CreateOrRecycleFrameResource();
SkImageInfo info = SkImageInfo::Make(
width_, height_, kN32_SkColorType,
image->IsPremultiplied() ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
RefPtr<ArrayBuffer> dst_buffer =
ArrayBuffer::CreateOrNull(width_ * height_, info.bytesPerPixel());
// If it fails to create a buffer for copying the pixel data, then exit early.
if (!dst_buffer)
return;
unsigned byte_length = dst_buffer->ByteLength();
RefPtr<Uint8Array> dst_pixels =
Uint8Array::Create(std::move(dst_buffer), 0, byte_length);
image->PaintImageForCurrentFrame().GetSkImage()->readPixels(
info, dst_pixels->Data(), info.minRowBytes(), 0, 0);
DCHECK(frame_resource->context_provider_wrapper_.get() ==
context_provider_wrapper.get() ||
!frame_resource->context_provider_wrapper_);
if (frame_resource->texture_id_ == 0u ||
!frame_resource->context_provider_wrapper_) {
frame_resource->context_provider_wrapper_ = context_provider_wrapper;
gl->GenTextures(1, &frame_resource->texture_id_);
gl->BindTexture(GL_TEXTURE_2D, frame_resource->texture_id_);
GLenum format =
(kN32_SkColorType == kRGBA_8888_SkColorType) ? GL_RGBA : GL_BGRA_EXT;
gl->TexImage2D(GL_TEXTURE_2D, 0, format, width_, height_, 0, format,
GL_UNSIGNED_BYTE, 0);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, format,
GL_UNSIGNED_BYTE, dst_pixels->Data());
gl->GenMailboxCHROMIUM(frame_resource->mailbox_.name);
gl->ProduceTextureCHROMIUM(GL_TEXTURE_2D, frame_resource->mailbox_.name);
}
const GLuint64 fence_sync = gl->InsertFenceSyncCHROMIUM();
gl->Flush();
gpu::SyncToken sync_token;
gl->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
resource.mailbox_holder =
gpu::MailboxHolder(frame_resource->mailbox_, sync_token, GL_TEXTURE_2D);
resource.read_lock_fences_enabled = false;
resource.is_software = false;
resources_.insert(next_resource_id_, std::move(frame_resource));
gr->resetContext(kTextureBinding_GrGLBackendState);
}
void OffscreenCanvasResourceProvider::
SetTransferableResourceToStaticBitmapImage(
viz::TransferableResource& resource,
RefPtr<StaticBitmapImage> image) {
DCHECK(image->IsTextureBacked());
DCHECK(image->IsValid());
image->EnsureMailbox(kVerifiedSyncToken);
resource.mailbox_holder = gpu::MailboxHolder(
image->GetMailbox(), image->GetSyncToken(), GL_TEXTURE_2D);
resource.read_lock_fences_enabled = false;
resource.is_software = false;
std::unique_ptr<FrameResource> frame_resource =
CreateOrRecycleFrameResource();
frame_resource->context_provider_wrapper_ = image->ContextProviderWrapper();
frame_resource->image_ = std::move(image);
resources_.insert(next_resource_id_, std::move(frame_resource));
}
OffscreenCanvasResourceProvider::FrameResource::~FrameResource() {
if (context_provider_wrapper_) {
gpu::gles2::GLES2Interface* gl =
context_provider_wrapper_->ContextProvider()->ContextGL();
if (texture_id_)
gl->DeleteTextures(1, &texture_id_);
if (image_id_)
gl->DestroyImageCHROMIUM(image_id_);
}
}
void OffscreenCanvasResourceProvider::ReclaimResources(
const WTF::Vector<viz::ReturnedResource>& resources) {
for (const auto& resource : resources) {
auto it = resources_.find(resource.id);
DCHECK(it != resources_.end());
if (it == resources_.end())
continue;
if (it->value->context_provider_wrapper_ && resource.sync_token.HasData()) {
it->value->context_provider_wrapper_->ContextProvider()
->ContextGL()
->WaitSyncTokenCHROMIUM(resource.sync_token.GetConstData());
}
ReclaimResourceInternal(it);
}
}
void OffscreenCanvasResourceProvider::ReclaimResource(unsigned resource_id) {
auto it = resources_.find(resource_id);
if (it != resources_.end()) {
ReclaimResourceInternal(it);
}
}
void OffscreenCanvasResourceProvider::ReclaimResourceInternal(
const ResourceMap::iterator& it) {
if (it->value->spare_lock_) {
it->value->spare_lock_ = false;
} else {
// Really reclaim the resources
recyclable_resource_ = std::move(it->value);
// release SkImage immediately since it is not recyclable
recyclable_resource_->image_ = nullptr;
resources_.erase(it);
}
}
} // namespace blink