blob: 788560d34df993d95078d961dcee198cb0bb55e9 [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/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/viz/common/resources/resource_format_utils.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"
using gpu::gles2::GLES2Interface;
namespace cc {
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,
shared_bitmap_manager,
gpu_memory_buffer_manager,
delegated_sync_points_required,
resource_settings,
kLayerTreeInitialResourceId) {}
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);
}
}
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;
}
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);
const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
gl->OrderingBarrierCHROMIUM();
gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, 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::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::TransferResource(
viz::internal::Resource* source,
viz::ResourceId id,
viz::TransferableResource* resource) {
DCHECK(!source->locked_for_write);
DCHECK(!source->lock_for_read_count);
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();
}
}
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();
}
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::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