blob: e49a200d09c68621b40f6e6e613f5249699b3883 [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 "cc/resources/layer_tree_resource_provider.h"
#include "base/bits.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "cc/resources/resource_util.h"
#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/common/resources/platform_color.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/resources/shared_bitmap_manager.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
#include "gpu/command_buffer/client/raster_interface.h"
#include "gpu/command_buffer/common/capabilities.h"
#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
#include "third_party/skia/include/core/SkCanvas.h"
using gpu::gles2::GLES2Interface;
namespace cc {
class TextureIdAllocator {
public:
TextureIdAllocator(GLES2Interface* gl,
size_t texture_id_allocation_chunk_size)
: gl_(gl),
id_allocation_chunk_size_(texture_id_allocation_chunk_size),
ids_(new GLuint[texture_id_allocation_chunk_size]),
next_id_index_(texture_id_allocation_chunk_size) {
DCHECK(id_allocation_chunk_size_);
DCHECK_LE(id_allocation_chunk_size_,
static_cast<size_t>(std::numeric_limits<int>::max()));
}
~TextureIdAllocator() {
if (gl_)
gl_->DeleteTextures(
static_cast<int>(id_allocation_chunk_size_ - next_id_index_),
ids_.get() + next_id_index_);
}
GLuint NextId() {
if (next_id_index_ == id_allocation_chunk_size_) {
gl_->GenTextures(static_cast<int>(id_allocation_chunk_size_), ids_.get());
next_id_index_ = 0;
}
return ids_[next_id_index_++];
}
private:
GLES2Interface* gl_;
const size_t id_allocation_chunk_size_;
std::unique_ptr<GLuint[]> ids_;
size_t next_id_index_;
DISALLOW_COPY_AND_ASSIGN(TextureIdAllocator);
};
LayerTreeResourceProvider::Settings::Settings(
viz::ContextProvider* compositor_context_provider,
bool delegated_sync_points_required,
const viz::ResourceSettings& resource_settings)
: yuv_highbit_resource_format(resource_settings.high_bit_for_testing
? viz::R16_EXT
: viz::LUMINANCE_8),
use_gpu_memory_buffer_resources(
resource_settings.use_gpu_memory_buffer_resources),
delegated_sync_points_required(delegated_sync_points_required) {
if (!compositor_context_provider) {
// Pick an arbitrary limit here similar to what hardware might.
max_texture_size = 16 * 1024;
best_texture_format = viz::RGBA_8888;
return;
}
const auto& caps = compositor_context_provider->ContextCapabilities();
use_texture_storage = caps.texture_storage;
use_texture_format_bgra = caps.texture_format_bgra8888;
use_texture_usage_hint = caps.texture_usage;
use_texture_npot = caps.texture_npot;
use_sync_query = caps.sync_query;
use_texture_storage_image = caps.texture_storage_image;
if (caps.disable_one_component_textures) {
yuv_resource_format = yuv_highbit_resource_format = viz::RGBA_8888;
} else {
yuv_resource_format = caps.texture_rg ? viz::RED_8 : viz::LUMINANCE_8;
if (resource_settings.use_r16_texture && caps.texture_norm16)
yuv_highbit_resource_format = viz::R16_EXT;
else if (caps.texture_half_float_linear)
yuv_highbit_resource_format = viz::LUMINANCE_F16;
}
GLES2Interface* gl = compositor_context_provider->ContextGL();
gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
best_texture_format =
viz::PlatformColor::BestSupportedTextureFormat(use_texture_format_bgra);
best_render_buffer_format = viz::PlatformColor::BestSupportedTextureFormat(
caps.render_buffer_format_bgra8888);
}
namespace {
// The resource id in LayerTreeResourceProvider starts from 1 to avoid
// conflicts with id from DisplayResourceProvider.
const unsigned int kLayerTreeInitialResourceId = 1;
} // namespace
struct LayerTreeResourceProvider::ImportedResource {
viz::TransferableResource resource;
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
int exported_count = 0;
bool marked_for_deletion = false;
gpu::SyncToken returned_sync_token;
bool returned_lost = false;
ImportedResource(viz::ResourceId id,
const viz::TransferableResource& resource,
std::unique_ptr<viz::SingleReleaseCallback> release_callback)
: resource(resource), release_callback(std::move(release_callback)) {
// Replace the |resource| id with the local id from this
// LayerTreeResourceProvider.
this->resource.id = id;
}
~ImportedResource() = default;
ImportedResource(ImportedResource&&) = default;
ImportedResource& operator=(ImportedResource&&) = default;
};
LayerTreeResourceProvider::LayerTreeResourceProvider(
viz::ContextProvider* compositor_context_provider,
viz::SharedBitmapManager* shared_bitmap_manager,
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
bool delegated_sync_points_required,
const viz::ResourceSettings& resource_settings)
: ResourceProvider(compositor_context_provider),
settings_(compositor_context_provider,
delegated_sync_points_required,
resource_settings),
shared_bitmap_manager_(shared_bitmap_manager),
gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
next_id_(kLayerTreeInitialResourceId) {
DCHECK(resource_settings.texture_id_allocation_chunk_size);
GLES2Interface* gl = ContextGL();
texture_id_allocator_ = std::make_unique<TextureIdAllocator>(
gl, resource_settings.texture_id_allocation_chunk_size);
}
LayerTreeResourceProvider::~LayerTreeResourceProvider() {
for (auto& pair : imported_resources_) {
ImportedResource& imported = pair.second;
// If the resource is exported we can't report when it can be used again
// once this class is destroyed, so consider the resource lost.
bool is_lost = imported.exported_count || imported.returned_lost;
imported.release_callback->Run(imported.returned_sync_token, is_lost);
}
texture_id_allocator_ = nullptr;
GLES2Interface* gl = ContextGL();
if (gl)
gl->Finish();
}
gpu::SyncToken LayerTreeResourceProvider::GenerateSyncTokenHelper(
gpu::gles2::GLES2Interface* gl) {
DCHECK(gl);
gpu::SyncToken sync_token;
gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
DCHECK(sync_token.HasData() ||
gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR);
return sync_token;
}
gpu::SyncToken LayerTreeResourceProvider::GenerateSyncTokenHelper(
gpu::raster::RasterInterface* ri) {
DCHECK(ri);
gpu::SyncToken sync_token;
ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
DCHECK(sync_token.HasData() ||
ri->GetGraphicsResetStatusKHR() != GL_NO_ERROR);
return sync_token;
}
gpu::SyncToken LayerTreeResourceProvider::GetSyncTokenForResources(
const ResourceIdArray& resource_ids) {
gpu::SyncToken latest_sync_token;
for (viz::ResourceId id : resource_ids) {
const gpu::SyncToken& sync_token = GetResource(id)->sync_token();
if (sync_token.release_count() > latest_sync_token.release_count())
latest_sync_token = sync_token;
}
return latest_sync_token;
}
gfx::ColorSpace LayerTreeResourceProvider::GetResourceColorSpaceForRaster(
const viz::internal::Resource* resource) const {
return resource->color_space;
}
void LayerTreeResourceProvider::PrepareSendToParent(
const ResourceIdArray& export_ids,
std::vector<viz::TransferableResource>* list) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
GLES2Interface* gl = ContextGL();
// This function goes through the array multiple times, store the resources
// as pointers so we don't have to look up the resource id multiple times.
// Make sure the maps do not change while these vectors are alive or they
// will become invalid.
std::vector<std::pair<viz::internal::Resource*, viz::ResourceId>> resources;
std::vector<ImportedResource*> imports;
resources.reserve(export_ids.size());
imports.reserve(export_ids.size());
for (const viz::ResourceId id : export_ids) {
auto it = imported_resources_.find(id);
if (it != imported_resources_.end())
imports.push_back(&it->second);
else
resources.push_back({GetResource(id), id});
}
DCHECK_EQ(resources.size() + imports.size(), export_ids.size());
// Lazily create any mailboxes and verify all unverified sync tokens.
std::vector<GLbyte*> unverified_sync_tokens;
std::vector<viz::internal::Resource*> need_synchronization_resources;
for (auto& pair : resources) {
viz::internal::Resource* resource = pair.first;
if (!resource->is_gpu_resource_type())
continue;
CreateMailbox(resource);
if (settings_.delegated_sync_points_required) {
if (resource->needs_sync_token()) {
need_synchronization_resources.push_back(resource);
} else if (!resource->sync_token().verified_flush()) {
unverified_sync_tokens.push_back(resource->GetSyncTokenData());
}
}
}
if (settings_.delegated_sync_points_required) {
for (ImportedResource* imported : imports) {
if (!imported->resource.is_software &&
!imported->resource.mailbox_holder.sync_token.verified_flush()) {
unverified_sync_tokens.push_back(
imported->resource.mailbox_holder.sync_token.GetData());
}
}
}
// Insert sync point to synchronize the mailbox creation or bound textures.
gpu::SyncToken new_sync_token;
if (!need_synchronization_resources.empty()) {
DCHECK(settings_.delegated_sync_points_required);
DCHECK(gl);
gl->GenUnverifiedSyncTokenCHROMIUM(new_sync_token.GetData());
unverified_sync_tokens.push_back(new_sync_token.GetData());
}
if (compositor_context_provider_)
compositor_context_provider_->ContextSupport()->FlushPendingWork();
if (!unverified_sync_tokens.empty()) {
DCHECK(settings_.delegated_sync_points_required);
DCHECK(gl);
gl->VerifySyncTokensCHROMIUM(unverified_sync_tokens.data(),
unverified_sync_tokens.size());
}
// Set sync token after verification.
for (viz::internal::Resource* resource : need_synchronization_resources) {
DCHECK(resource->is_gpu_resource_type());
resource->UpdateSyncToken(new_sync_token);
resource->SetSynchronized();
}
// Transfer Resources.
for (size_t i = 0; i < resources.size(); ++i) {
viz::internal::Resource* source = resources[i].first;
const viz::ResourceId id = resources[i].second;
DCHECK(!settings_.delegated_sync_points_required ||
!source->needs_sync_token());
DCHECK(!settings_.delegated_sync_points_required ||
viz::internal::Resource::LOCALLY_USED !=
source->synchronization_state());
viz::TransferableResource resource;
TransferResource(source, id, &resource);
source->exported_count++;
list->push_back(std::move(resource));
}
for (ImportedResource* imported : imports) {
list->push_back(imported->resource);
imported->exported_count++;
}
}
void LayerTreeResourceProvider::ReceiveReturnsFromParent(
const std::vector<viz::ReturnedResource>& resources) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
GLES2Interface* gl = ContextGL();
for (const viz::ReturnedResource& returned : resources) {
viz::ResourceId local_id = returned.id;
auto import_it = imported_resources_.find(local_id);
if (import_it != imported_resources_.end()) {
ImportedResource& imported = import_it->second;
DCHECK_GE(imported.exported_count, returned.count);
imported.exported_count -= returned.count;
imported.returned_lost |= returned.lost;
if (imported.exported_count)
continue;
if (returned.sync_token.HasData()) {
DCHECK(!imported.resource.is_software);
imported.returned_sync_token = returned.sync_token;
}
if (imported.marked_for_deletion) {
imported.release_callback->Run(imported.returned_sync_token,
imported.returned_lost);
imported_resources_.erase(import_it);
}
continue;
}
auto map_iterator = resources_.find(local_id);
DCHECK(map_iterator != resources_.end());
// Resource was already lost (e.g. it belonged to a child that was
// destroyed).
// TODO(danakj): Remove this. There is no "child" here anymore, and
// lost resources are still in the map until exported_count == 0.
if (map_iterator == resources_.end())
continue;
viz::internal::Resource* resource = &map_iterator->second;
DCHECK_GE(resource->exported_count, returned.count);
resource->exported_count -= returned.count;
resource->lost |= returned.lost;
if (resource->exported_count)
continue;
if (returned.sync_token.HasData()) {
DCHECK(!resource->has_shared_bitmap_id);
if (resource->origin == viz::internal::Resource::INTERNAL) {
DCHECK(resource->gl_id);
gl->WaitSyncTokenCHROMIUM(returned.sync_token.GetConstData());
resource->SetSynchronized();
} else {
DCHECK(!resource->gl_id);
resource->UpdateSyncToken(returned.sync_token);
}
}
if (!resource->marked_for_deletion)
continue;
// The resource belongs to this LayerTreeResourceProvider, so it can be
// destroyed.
DeleteResourceInternal(map_iterator, NORMAL);
}
}
viz::ResourceId LayerTreeResourceProvider::CreateGpuTextureResource(
const gfx::Size& size,
viz::ResourceTextureHint hint,
viz::ResourceFormat format,
const gfx::ColorSpace& color_space) {
DCHECK(compositor_context_provider_);
DCHECK(!size.IsEmpty());
DCHECK_LE(size.width(), settings_.max_texture_size);
DCHECK_LE(size.height(), settings_.max_texture_size);
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(IsTextureFormatSupported(format));
bool use_overlay = settings_.use_gpu_memory_buffer_resources ||
(hint & viz::ResourceTextureHint::kOverlay);
DCHECK(!use_overlay || !(hint & viz::ResourceTextureHint::kMipmap));
viz::ResourceId id = next_id_++;
viz::internal::Resource* resource =
InsertResource(id, viz::internal::Resource(
size, viz::internal::Resource::INTERNAL, hint,
viz::ResourceType::kTexture, format, color_space));
if (use_overlay && settings_.use_texture_storage_image &&
IsGpuMemoryBufferFormatSupported(format, gfx::BufferUsage::SCANOUT)) {
resource->usage = gfx::BufferUsage::SCANOUT;
resource->target = GetImageTextureTarget(
compositor_context_provider_->ContextCapabilities(), resource->usage,
format);
resource->buffer_format = BufferFormat(format);
resource->is_overlay_candidate = true;
}
return id;
}
viz::ResourceId LayerTreeResourceProvider::CreateGpuMemoryBufferResource(
const gfx::Size& size,
viz::ResourceTextureHint hint,
viz::ResourceFormat format,
gfx::BufferUsage usage,
const gfx::ColorSpace& color_space) {
DCHECK(compositor_context_provider_);
DCHECK(!size.IsEmpty());
DCHECK(compositor_context_provider_);
DCHECK_LE(size.width(), settings_.max_texture_size);
DCHECK_LE(size.height(), settings_.max_texture_size);
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
viz::ResourceId id = next_id_++;
viz::internal::Resource* resource = InsertResource(
id, viz::internal::Resource(size, viz::internal::Resource::INTERNAL, hint,
viz::ResourceType::kGpuMemoryBuffer, format,
color_space));
resource->target = GetImageTextureTarget(
compositor_context_provider_->ContextCapabilities(), usage, format);
resource->buffer_format = BufferFormat(format);
resource->usage = usage;
resource->is_overlay_candidate = true;
// GpuMemoryBuffer provides direct access to the memory used by the GPU. Read
// lock fences are required to ensure that we're not trying to map a buffer
// that is currently in-use by the GPU.
resource->read_lock_fences_enabled = true;
return id;
}
viz::ResourceId LayerTreeResourceProvider::CreateBitmapResource(
const gfx::Size& size,
const gfx::ColorSpace& color_space) {
DCHECK(!compositor_context_provider_);
DCHECK(!size.IsEmpty());
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// TODO(danakj): Allocate this outside ResourceProvider.
std::unique_ptr<viz::SharedBitmap> bitmap =
shared_bitmap_manager_->AllocateSharedBitmap(size);
DCHECK(bitmap);
DCHECK(bitmap->pixels());
viz::ResourceId id = next_id_++;
viz::internal::Resource* resource = InsertResource(
id, viz::internal::Resource(size, viz::internal::Resource::INTERNAL,
viz::ResourceTextureHint::kDefault,
viz::ResourceType::kBitmap, viz::RGBA_8888,
color_space));
resource->SetSharedBitmap(bitmap.get());
resource->owned_shared_bitmap = std::move(bitmap);
return id;
}
void LayerTreeResourceProvider::DeleteResource(viz::ResourceId id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ResourceMap::iterator it = resources_.find(id);
CHECK(it != resources_.end());
viz::internal::Resource* resource = &it->second;
DCHECK(!resource->marked_for_deletion);
DCHECK_EQ(resource->imported_count, 0);
DCHECK(!resource->locked_for_write);
if (resource->exported_count > 0) {
resource->marked_for_deletion = true;
return;
} else {
DeleteResourceInternal(it, NORMAL);
}
}
viz::ResourceId LayerTreeResourceProvider::ImportResource(
const viz::TransferableResource& resource,
std::unique_ptr<viz::SingleReleaseCallback> release_callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
viz::ResourceId id = next_id_++;
auto result = imported_resources_.emplace(
id, ImportedResource(id, resource, std::move(release_callback)));
DCHECK(result.second); // If false, the id was already in the map.
return id;
}
void LayerTreeResourceProvider::RemoveImportedResource(viz::ResourceId id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto it = imported_resources_.find(id);
DCHECK(it != imported_resources_.end());
ImportedResource& imported = it->second;
imported.marked_for_deletion = true;
if (imported.exported_count == 0) {
imported.release_callback->Run(imported.returned_sync_token,
imported.returned_lost);
imported_resources_.erase(it);
}
}
void LayerTreeResourceProvider::CopyToResource(viz::ResourceId id,
const uint8_t* image,
const gfx::Size& image_size) {
viz::internal::Resource* resource = GetResource(id);
DCHECK(!resource->locked_for_write);
DCHECK_EQ(resource->origin, viz::internal::Resource::INTERNAL);
DCHECK_NE(resource->synchronization_state(),
viz::internal::Resource::NEEDS_WAIT);
DCHECK_EQ(resource->exported_count, 0);
DCHECK_EQ(image_size.width(), resource->size.width());
DCHECK_EQ(image_size.height(), resource->size.height());
if (resource->type == viz::ResourceType::kBitmap) {
DCHECK_EQ(viz::ResourceType::kBitmap, resource->type);
DCHECK(resource->allocated);
DCHECK_EQ(viz::RGBA_8888, resource->format);
SkImageInfo source_info =
SkImageInfo::MakeN32Premul(image_size.width(), image_size.height());
size_t image_stride = image_size.width() * 4;
ScopedWriteLockSoftware lock(this, id);
SkCanvas dest(lock.sk_bitmap());
dest.writePixels(source_info, image, image_stride, 0, 0);
} else {
// No sync token needed because the lock will set synchronization state to
// LOCALLY_USED and a sync token will be generated in PrepareSendToParent.
ScopedWriteLockGL lock(this, id);
GLuint texture_id = lock.GetTexture();
DCHECK(texture_id);
GLES2Interface* gl = ContextGL();
DCHECK(gl);
gl->BindTexture(resource->target, texture_id);
if (resource->format == viz::ETC1) {
DCHECK_EQ(resource->target, static_cast<GLenum>(GL_TEXTURE_2D));
int image_bytes =
ResourceUtil::CheckedSizeInBytes<int>(image_size, viz::ETC1);
gl->CompressedTexImage2D(resource->target, 0, GLInternalFormat(viz::ETC1),
image_size.width(), image_size.height(), 0,
image_bytes, image);
lock.set_allocated();
} else {
gl->TexSubImage2D(resource->target, 0, 0, 0, image_size.width(),
image_size.height(), GLDataFormat(resource->format),
GLDataType(resource->format), image);
}
}
DCHECK(resource->allocated);
}
void LayerTreeResourceProvider::CreateForTesting(viz::ResourceId id) {
CreateTexture(GetResource(id));
}
void LayerTreeResourceProvider::CreateTexture(
viz::internal::Resource* resource) {
if (!resource->is_gpu_resource_type())
return;
if (resource->gl_id)
return;
DCHECK_EQ(resource->origin, viz::internal::Resource::INTERNAL);
DCHECK(resource->mailbox.IsZero());
resource->gl_id = texture_id_allocator_->NextId();
DCHECK(resource->gl_id);
GLES2Interface* gl = ContextGL();
DCHECK(gl);
// Create and set texture properties. Allocation is delayed until needed.
gl->BindTexture(resource->target, resource->gl_id);
gl->TexParameteri(resource->target, GL_TEXTURE_MIN_FILTER,
resource->original_filter);
gl->TexParameteri(resource->target, GL_TEXTURE_MAG_FILTER,
resource->original_filter);
gl->TexParameteri(resource->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri(resource->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (settings_.use_texture_usage_hint &&
(resource->hint & viz::ResourceTextureHint::kFramebuffer)) {
gl->TexParameteri(resource->target, GL_TEXTURE_USAGE_ANGLE,
GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
}
}
void LayerTreeResourceProvider::CreateMailbox(
viz::internal::Resource* resource) {
if (!resource->is_gpu_resource_type())
return;
if (!resource->mailbox.IsZero())
return;
CreateTexture(resource);
DCHECK(resource->gl_id);
DCHECK_EQ(resource->origin, viz::internal::Resource::INTERNAL);
gpu::gles2::GLES2Interface* gl = ContextGL();
DCHECK(gl);
gl->GenMailboxCHROMIUM(resource->mailbox.name);
gl->ProduceTextureDirectCHROMIUM(resource->gl_id, resource->mailbox.name);
resource->SetLocallyUsed();
}
void LayerTreeResourceProvider::CreateAndBindImage(
viz::internal::Resource* resource) {
DCHECK(resource->gpu_memory_buffer);
#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
// TODO(reveman): This avoids a performance problem on ARM ChromeOS
// devices. This only works with shared memory backed buffers.
// https://crbug.com/580166
DCHECK_EQ(resource->gpu_memory_buffer->GetHandle().type,
gfx::SHARED_MEMORY_BUFFER);
#endif
CreateTexture(resource);
gpu::gles2::GLES2Interface* gl = ContextGL();
DCHECK(gl);
gl->BindTexture(resource->target, resource->gl_id);
if (!resource->image_id) {
resource->image_id = gl->CreateImageCHROMIUM(
resource->gpu_memory_buffer->AsClientBuffer(), resource->size.width(),
resource->size.height(), GLInternalFormat(resource->format));
DCHECK(resource->image_id ||
gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR);
gl->BindTexImage2DCHROMIUM(resource->target, resource->image_id);
} else {
gl->ReleaseTexImage2DCHROMIUM(resource->target, resource->image_id);
gl->BindTexImage2DCHROMIUM(resource->target, resource->image_id);
}
}
void LayerTreeResourceProvider::AllocateForTesting(viz::ResourceId id) {
viz::internal::Resource* resource = GetResource(id);
if (!resource->allocated) {
// Software and external resources are marked allocated on creation.
DCHECK(resource->is_gpu_resource_type());
DCHECK_EQ(resource->origin, viz::internal::Resource::INTERNAL);
ScopedWriteLockGL resource_lock(this, id);
resource_lock.GetTexture(); // Allocates texture.
}
}
void LayerTreeResourceProvider::TransferResource(
viz::internal::Resource* source,
viz::ResourceId id,
viz::TransferableResource* resource) {
DCHECK(!source->locked_for_write);
DCHECK(source->allocated);
resource->id = id;
resource->format = source->format;
resource->buffer_format = source->buffer_format;
resource->filter = source->filter;
resource->size = source->size;
resource->read_lock_fences_enabled = source->read_lock_fences_enabled;
resource->is_overlay_candidate = source->is_overlay_candidate;
resource->color_space = source->color_space;
if (source->type == viz::ResourceType::kBitmap) {
DCHECK(source->shared_bitmap);
resource->mailbox_holder.mailbox = source->shared_bitmap_id;
resource->is_software = true;
resource->shared_bitmap_sequence_number =
source->shared_bitmap->sequence_number();
} else {
DCHECK(!source->mailbox.IsZero());
// This is either an external resource, or a compositor resource that we
// already exported. Make sure to forward the sync point that we were given.
resource->mailbox_holder.mailbox = source->mailbox;
resource->mailbox_holder.texture_target = source->target;
resource->mailbox_holder.sync_token = source->sync_token();
}
}
bool LayerTreeResourceProvider::CanLockForWrite(viz::ResourceId id) {
viz::internal::Resource* resource = GetResource(id);
return !resource->locked_for_write && !resource->exported_count &&
resource->origin == viz::internal::Resource::INTERNAL &&
!resource->lost;
}
viz::internal::Resource* LayerTreeResourceProvider::LockForWrite(
viz::ResourceId id) {
DCHECK(CanLockForWrite(id));
viz::internal::Resource* resource = GetResource(id);
WaitSyncTokenInternal(resource);
resource->SetLocallyUsed();
resource->locked_for_write = true;
resource->mipmap_state = viz::internal::Resource::INVALID;
return resource;
}
void LayerTreeResourceProvider::UnlockForWrite(
viz::internal::Resource* resource) {
DCHECK(resource->locked_for_write);
DCHECK_EQ(resource->exported_count, 0);
DCHECK(resource->origin == viz::internal::Resource::INTERNAL);
resource->locked_for_write = false;
}
void LayerTreeResourceProvider::FlushPendingDeletions() const {
if (auto* gl = ContextGL())
gl->ShallowFlushCHROMIUM();
}
GLenum LayerTreeResourceProvider::GetImageTextureTarget(
const gpu::Capabilities& caps,
gfx::BufferUsage usage,
viz::ResourceFormat format) const {
return gpu::GetBufferTextureTarget(usage, BufferFormat(format), caps);
}
bool LayerTreeResourceProvider::IsTextureFormatSupported(
viz::ResourceFormat format) const {
gpu::Capabilities caps;
if (compositor_context_provider_)
caps = compositor_context_provider_->ContextCapabilities();
switch (format) {
case viz::ALPHA_8:
case viz::RGBA_4444:
case viz::RGBA_8888:
case viz::RGB_565:
case viz::LUMINANCE_8:
return true;
case viz::BGRA_8888:
return caps.texture_format_bgra8888;
case viz::ETC1:
return caps.texture_format_etc1;
case viz::RED_8:
return caps.texture_rg;
case viz::R16_EXT:
return caps.texture_norm16;
case viz::LUMINANCE_F16:
case viz::RGBA_F16:
return caps.texture_half_float_linear;
}
NOTREACHED();
return false;
}
bool LayerTreeResourceProvider::IsRenderBufferFormatSupported(
viz::ResourceFormat format) const {
gpu::Capabilities caps;
if (compositor_context_provider_)
caps = compositor_context_provider_->ContextCapabilities();
switch (format) {
case viz::RGBA_4444:
case viz::RGBA_8888:
case viz::RGB_565:
return true;
case viz::BGRA_8888:
return caps.render_buffer_format_bgra8888;
case viz::RGBA_F16:
// TODO(ccameron): This will always return false on pixel tests, which
// makes it un-test-able until we upgrade Mesa.
// https://crbug.com/687720
return caps.texture_half_float_linear &&
caps.color_buffer_half_float_rgba;
case viz::LUMINANCE_8:
case viz::ALPHA_8:
case viz::RED_8:
case viz::ETC1:
case viz::LUMINANCE_F16:
case viz::R16_EXT:
// We don't currently render into these formats. If we need to render into
// these eventually, we should expand this logic.
return false;
}
NOTREACHED();
return false;
}
bool LayerTreeResourceProvider::IsGpuMemoryBufferFormatSupported(
viz::ResourceFormat format,
gfx::BufferUsage usage) const {
switch (format) {
case viz::BGRA_8888:
case viz::RED_8:
case viz::R16_EXT:
case viz::RGBA_4444:
case viz::RGBA_8888:
case viz::ETC1:
case viz::RGBA_F16:
return true;
// These formats have no BufferFormat equivalent.
case viz::ALPHA_8:
case viz::LUMINANCE_8:
case viz::RGB_565:
case viz::LUMINANCE_F16:
return false;
}
NOTREACHED();
return false;
}
viz::ResourceFormat LayerTreeResourceProvider::YuvResourceFormat(
int bits) const {
if (bits > 8) {
return settings_.yuv_highbit_resource_format;
} else {
return settings_.yuv_resource_format;
}
}
LayerTreeResourceProvider::ScopedWriteLockGpu::ScopedWriteLockGpu(
LayerTreeResourceProvider* resource_provider,
viz::ResourceId resource_id)
: resource_provider_(resource_provider), resource_id_(resource_id) {
viz::internal::Resource* resource =
resource_provider->LockForWrite(resource_id);
DCHECK_EQ(resource->type, viz::ResourceType::kTexture);
resource_provider->CreateTexture(resource);
size_ = resource->size;
format_ = resource->format;
color_space_ = resource_provider_->GetResourceColorSpaceForRaster(resource);
texture_id_ = resource->gl_id;
target_ = resource->target;
hint_ = resource->hint;
mailbox_ = resource->mailbox;
is_overlay_ = resource->is_overlay_candidate;
allocated_ = resource->allocated;
}
LayerTreeResourceProvider::ScopedWriteLockGpu::~ScopedWriteLockGpu() {
viz::internal::Resource* resource =
resource_provider_->GetResource(resource_id_);
DCHECK(resource->locked_for_write);
resource->allocated = allocated_;
resource->mailbox = mailbox_;
// Don't set empty sync token otherwise resource will be marked synchronized.
if (has_sync_token_)
resource->UpdateSyncToken(sync_token_);
if (synchronized_)
resource->SetSynchronized();
if (generate_mipmap_)
resource->SetGenerateMipmap();
resource_provider_->UnlockForWrite(resource);
}
GrPixelConfig LayerTreeResourceProvider::ScopedWriteLockGpu::PixelConfig()
const {
return ToGrPixelConfig(format_);
}
void LayerTreeResourceProvider::ScopedWriteLockGpu::CreateMailbox() {
if (!mailbox_.IsZero())
return;
gpu::gles2::GLES2Interface* gl = resource_provider_->ContextGL();
DCHECK(gl);
gl->GenMailboxCHROMIUM(mailbox_.name);
gl->ProduceTextureDirectCHROMIUM(texture_id_, mailbox_.name);
}
LayerTreeResourceProvider::ScopedWriteLockGL::ScopedWriteLockGL(
LayerTreeResourceProvider* resource_provider,
viz::ResourceId resource_id)
: ScopedWriteLockGpu(resource_provider, resource_id) {}
LayerTreeResourceProvider::ScopedWriteLockGL::~ScopedWriteLockGL() {}
GLuint LayerTreeResourceProvider::ScopedWriteLockGL::GetTexture() {
LazyAllocate(resource_provider_->ContextGL(), texture_id_);
return texture_id_;
}
void LayerTreeResourceProvider::ScopedWriteLockGL::LazyAllocate(
gpu::gles2::GLES2Interface* gl,
GLuint texture_id) {
// ETC1 resources cannot be preallocated.
if (format_ == viz::ETC1)
return;
if (allocated_)
return;
allocated_ = true;
const Settings& settings = resource_provider_->settings_;
gl->BindTexture(target_, texture_id);
if (is_overlay_) {
DCHECK(settings.use_texture_storage_image);
gl->TexStorage2DImageCHROMIUM(target_, viz::TextureStorageFormat(format_),
GL_SCANOUT_CHROMIUM, size_.width(),
size_.height());
if (color_space_.IsValid()) {
gl->SetColorSpaceMetadataCHROMIUM(
texture_id, reinterpret_cast<GLColorSpace>(&color_space_));
}
} else if (settings.use_texture_storage) {
GLint levels = 1;
if (settings.use_texture_npot &&
(hint_ & viz::ResourceTextureHint::kMipmap)) {
levels += base::bits::Log2Floor(std::max(size_.width(), size_.height()));
}
gl->TexStorage2DEXT(target_, levels, viz::TextureStorageFormat(format_),
size_.width(), size_.height());
} else {
gl->TexImage2D(target_, 0, GLInternalFormat(format_), size_.width(),
size_.height(), 0, GLDataFormat(format_),
GLDataType(format_), nullptr);
}
}
LayerTreeResourceProvider::ScopedWriteLockRaster::ScopedWriteLockRaster(
LayerTreeResourceProvider* resource_provider,
viz::ResourceId resource_id)
: ScopedWriteLockGpu(resource_provider, resource_id) {}
LayerTreeResourceProvider::ScopedWriteLockRaster::~ScopedWriteLockRaster() {}
GLuint LayerTreeResourceProvider::ScopedWriteLockRaster::ConsumeTexture(
gpu::raster::RasterInterface* ri) {
DCHECK(ri);
DCHECK(!mailbox_.IsZero());
GLuint texture_id = ri->CreateAndConsumeTextureCHROMIUM(mailbox_.name);
DCHECK(texture_id);
LazyAllocate(ri, texture_id);
return texture_id;
}
void LayerTreeResourceProvider::ScopedWriteLockRaster::LazyAllocate(
gpu::raster::RasterInterface* ri,
GLuint texture_id) {
// ETC1 resources cannot be preallocated.
if (format_ == viz::ETC1)
return;
if (allocated_)
return;
allocated_ = true;
ri->BindTexture(target_, texture_id);
ri->TexStorageForRaster(
target_, format_, size_.width(), size_.height(),
is_overlay_ ? gpu::raster::kOverlay : gpu::raster::kNone);
if (is_overlay_ && color_space_.IsValid()) {
ri->SetColorSpaceMetadataCHROMIUM(
texture_id, reinterpret_cast<GLColorSpace>(&color_space_));
}
}
LayerTreeResourceProvider::ScopedWriteLockGpuMemoryBuffer ::
ScopedWriteLockGpuMemoryBuffer(LayerTreeResourceProvider* resource_provider,
viz::ResourceId resource_id)
: resource_provider_(resource_provider), resource_id_(resource_id) {
viz::internal::Resource* resource =
resource_provider->LockForWrite(resource_id);
DCHECK_EQ(resource->type, viz::ResourceType::kGpuMemoryBuffer);
size_ = resource->size;
format_ = resource->format;
usage_ = resource->usage;
color_space_ = resource_provider->GetResourceColorSpaceForRaster(resource);
gpu_memory_buffer_ = std::move(resource->gpu_memory_buffer);
}
LayerTreeResourceProvider::ScopedWriteLockGpuMemoryBuffer::
~ScopedWriteLockGpuMemoryBuffer() {
viz::internal::Resource* resource =
resource_provider_->GetResource(resource_id_);
// Avoid crashing in release builds if GpuMemoryBuffer allocation fails.
// http://crbug.com/554541
if (gpu_memory_buffer_) {
resource->gpu_memory_buffer = std::move(gpu_memory_buffer_);
resource->allocated = true;
resource_provider_->CreateAndBindImage(resource);
}
resource_provider_->UnlockForWrite(resource);
}
gfx::GpuMemoryBuffer* LayerTreeResourceProvider::
ScopedWriteLockGpuMemoryBuffer::GetGpuMemoryBuffer() {
if (!gpu_memory_buffer_) {
gpu_memory_buffer_ =
resource_provider_->gpu_memory_buffer_manager()->CreateGpuMemoryBuffer(
size_, BufferFormat(format_), usage_, gpu::kNullSurfaceHandle);
// Avoid crashing in release builds if GpuMemoryBuffer allocation fails.
// http://crbug.com/554541
if (gpu_memory_buffer_ && color_space_.IsValid())
gpu_memory_buffer_->SetColorSpace(color_space_);
}
return gpu_memory_buffer_.get();
}
LayerTreeResourceProvider::ScopedWriteLockSoftware::ScopedWriteLockSoftware(
LayerTreeResourceProvider* resource_provider,
viz::ResourceId resource_id)
: resource_provider_(resource_provider), resource_id_(resource_id) {
viz::internal::Resource* resource =
resource_provider->LockForWrite(resource_id);
resource_provider->PopulateSkBitmapWithResource(&sk_bitmap_, resource);
color_space_ = resource_provider->GetResourceColorSpaceForRaster(resource);
DCHECK(valid());
}
LayerTreeResourceProvider::ScopedWriteLockSoftware::~ScopedWriteLockSoftware() {
viz::internal::Resource* resource =
resource_provider_->GetResource(resource_id_);
resource->SetSynchronized();
resource_provider_->UnlockForWrite(resource);
}
LayerTreeResourceProvider::ScopedSkSurface::ScopedSkSurface(
GrContext* gr_context,
GLuint texture_id,
GLenum texture_target,
const gfx::Size& size,
viz::ResourceFormat format,
bool use_distance_field_text,
bool can_use_lcd_text,
int msaa_sample_count) {
GrGLTextureInfo texture_info;
texture_info.fID = texture_id;
texture_info.fTarget = texture_target;
GrBackendTexture backend_texture(size.width(), size.height(),
ToGrPixelConfig(format), texture_info);
uint32_t flags =
use_distance_field_text ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0;
// Use unknown pixel geometry to disable LCD text.
SkSurfaceProps surface_props(flags, kUnknown_SkPixelGeometry);
if (can_use_lcd_text) {
// LegacyFontHost will get LCD text and skia figures out what type to use.
surface_props =
SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
}
surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget(
gr_context, backend_texture, kTopLeft_GrSurfaceOrigin, msaa_sample_count,
nullptr, &surface_props);
}
LayerTreeResourceProvider::ScopedSkSurface::~ScopedSkSurface() {
if (surface_)
surface_->prepareForExternalIO();
}
void LayerTreeResourceProvider::ValidateResource(viz::ResourceId id) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(id);
DCHECK(resources_.find(id) != resources_.end() ||
imported_resources_.find(id) != imported_resources_.end());
}
bool LayerTreeResourceProvider::InUseByConsumer(viz::ResourceId id) {
auto it = imported_resources_.find(id);
if (it != imported_resources_.end()) {
ImportedResource& imported = it->second;
return imported.exported_count > 0 || imported.returned_lost;
}
viz::internal::Resource* resource = GetResource(id);
return resource->exported_count > 0 || resource->lost;
}
bool LayerTreeResourceProvider::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
// Imported resources should be tracked in the client where they
// originated, as this code has only a name to refer to them and
// is not keeping them alive.
// Non-imported resources are tracked in the base class.
return ResourceProvider::OnMemoryDump(args, pmd);
}
} // namespace cc