blob: a4e17f452318d56deba482c33e0c31bed6d654d9 [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 "components/viz/service/display/display_resource_provider.h"
#include "base/atomic_sequence_num.h"
#include "base/numerics/safe_math.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_event.h"
#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/resources/resource_sizes.h"
#include "components/viz/service/display/shared_bitmap_manager.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "ui/gl/trace_util.h"
using gpu::gles2::GLES2Interface;
namespace viz {
namespace {
// Generates process-unique IDs to use for tracing resources.
base::AtomicSequenceNumber g_next_display_resource_provider_tracing_id;
} // namespace
static GLint GetActiveTextureUnit(GLES2Interface* gl) {
GLint active_unit = 0;
gl->GetIntegerv(GL_ACTIVE_TEXTURE, &active_unit);
return active_unit;
}
class ScopedSetActiveTexture {
public:
ScopedSetActiveTexture(GLES2Interface* gl, GLenum unit)
: gl_(gl), unit_(unit) {
DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_));
if (unit_ != GL_TEXTURE0)
gl_->ActiveTexture(unit_);
}
~ScopedSetActiveTexture() {
// Active unit being GL_TEXTURE0 is effectively the ground state.
if (unit_ != GL_TEXTURE0)
gl_->ActiveTexture(GL_TEXTURE0);
}
private:
GLES2Interface* gl_;
GLenum unit_;
};
DisplayResourceProvider::DisplayResourceProvider(
Mode mode,
ContextProvider* compositor_context_provider,
SharedBitmapManager* shared_bitmap_manager)
: mode_(mode),
compositor_context_provider_(compositor_context_provider),
shared_bitmap_manager_(shared_bitmap_manager),
tracing_id_(g_next_display_resource_provider_tracing_id.GetNext()) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// If no ContextProvider, then we are doing software compositing and a
// SharedBitmapManager must be given.
DCHECK(mode_ == kGpu || shared_bitmap_manager);
// In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview).
// Don't register a dump provider in these cases.
// TODO(crbug.com/517156): Get this working in Android Webview.
if (base::ThreadTaskRunnerHandle::IsSet()) {
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "cc::ResourceProvider", base::ThreadTaskRunnerHandle::Get());
}
}
DisplayResourceProvider::~DisplayResourceProvider() {
while (!children_.empty())
DestroyChildInternal(children_.begin(), FOR_SHUTDOWN);
GLES2Interface* gl = ContextGL();
if (gl)
gl->Finish();
while (!resources_.empty())
DeleteResourceInternal(resources_.begin(), FOR_SHUTDOWN);
if (compositor_context_provider_) {
// Check that all GL resources has been deleted.
for (const auto& pair : resources_)
DCHECK(!pair.second.is_gpu_resource_type());
}
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this);
}
bool DisplayResourceProvider::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (const auto& resource_entry : resources_) {
const auto& resource = resource_entry.second;
bool backing_memory_allocated = false;
if (resource.transferable.is_software)
backing_memory_allocated = !!resource.shared_bitmap;
else
backing_memory_allocated = !!resource.gl_id;
if (!backing_memory_allocated) {
// Don't log unallocated resources - they have no backing memory.
continue;
}
// ResourceIds are not process-unique, so log with the ResourceProvider's
// unique id.
std::string dump_name =
base::StringPrintf("cc/resource_memory/provider_%d/resource_%d",
tracing_id_, resource_entry.first);
base::trace_event::MemoryAllocatorDump* dump =
pmd->CreateAllocatorDump(dump_name);
// Texture resources may not come with a size, in which case don't report
// one.
if (!resource.transferable.size.IsEmpty()) {
uint64_t total_bytes = ResourceSizes::UncheckedSizeInBytesAligned<size_t>(
resource.transferable.size, resource.transferable.format);
dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes,
static_cast<uint64_t>(total_bytes));
}
// Resources may be shared across processes and require a shared GUID to
// prevent double counting the memory.
base::trace_event::MemoryAllocatorDumpGuid guid;
base::UnguessableToken shared_memory_guid;
if (resource.transferable.is_software) {
shared_memory_guid = resource.shared_bitmap_tracing_guid;
} else {
guid = gl::GetGLTextureClientGUIDForTracing(
compositor_context_provider_->ContextSupport()
->ShareGroupTracingGUID(),
resource.gl_id);
}
DCHECK(!shared_memory_guid.is_empty() || !guid.empty());
// The client that owns the resource will use a higher importance (2), and
// the GPU service will use a lower one (0).
const int importance = 1;
if (!shared_memory_guid.is_empty()) {
pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shared_memory_guid,
importance);
} else {
pmd->CreateSharedGlobalAllocatorDump(guid);
pmd->AddOwnershipEdge(dump->guid(), guid, importance);
}
}
return true;
}
#if defined(OS_ANDROID)
void DisplayResourceProvider::SendPromotionHints(
const OverlayCandidateList::PromotionHintInfoMap& promotion_hints) {
GLES2Interface* gl = ContextGL();
if (!gl)
return;
for (const auto& id : wants_promotion_hints_set_) {
auto it = resources_.find(id);
if (it == resources_.end())
continue;
if (it->second.marked_for_deletion)
continue;
const ChildResource* resource = LockForRead(id);
// TODO(ericrk): We should never fail LockForRead, but we appear to be
// doing so on Android in rare cases. Handle this gracefully until a better
// solution can be found. https://crbug.com/811858
if (!resource)
return;
DCHECK(resource->transferable.wants_promotion_hint);
// Insist that this is backed by a GPU texture.
if (resource->is_gpu_resource_type()) {
DCHECK(resource->gl_id);
auto iter = promotion_hints.find(id);
bool promotable = iter != promotion_hints.end();
gl->OverlayPromotionHintCHROMIUM(resource->gl_id, promotable,
promotable ? iter->second.x() : 0,
promotable ? iter->second.y() : 0,
promotable ? iter->second.width() : 0,
promotable ? iter->second.height() : 0);
}
UnlockForRead(id);
}
}
bool DisplayResourceProvider::IsBackedBySurfaceTexture(ResourceId id) {
ChildResource* resource = GetResource(id);
return resource->transferable.is_backed_by_surface_texture;
}
bool DisplayResourceProvider::WantsPromotionHintForTesting(ResourceId id) {
return wants_promotion_hints_set_.count(id) > 0;
}
size_t DisplayResourceProvider::CountPromotionHintRequestsForTesting() {
return wants_promotion_hints_set_.size();
}
#endif
bool DisplayResourceProvider::IsOverlayCandidate(ResourceId id) {
ChildResource* resource = TryGetResource(id);
// TODO(ericrk): We should never fail TryGetResource, but we appear to
// be doing so on Android in rare cases. Handle this gracefully until a
// better solution can be found. https://crbug.com/811858
return resource && resource->transferable.is_overlay_candidate;
}
bool DisplayResourceProvider::IsResourceSoftwareBacked(ResourceId id) {
return GetResource(id)->transferable.is_software;
}
GLenum DisplayResourceProvider::GetResourceTextureTarget(ResourceId id) {
return GetResource(id)->transferable.mailbox_holder.texture_target;
}
gfx::BufferFormat DisplayResourceProvider::GetBufferFormat(ResourceId id) {
ChildResource* resource = GetResource(id);
return resource->transferable.buffer_format;
}
void DisplayResourceProvider::WaitSyncToken(ResourceId id) {
ChildResource* resource = TryGetResource(id);
// TODO(ericrk): We should never fail TryGetResource, but we appear to
// be doing so on Android in rare cases. Handle this gracefully until a
// better solution can be found. https://crbug.com/811858
if (!resource)
return;
WaitSyncTokenInternal(resource);
#if defined(OS_ANDROID)
// Now that the resource is synced, we may send it a promotion hint. We could
// sync all |wants_promotion_hint| resources elsewhere, and send 'no' to all
// resources that weren't used. However, there's no real advantage.
if (resource->transferable.wants_promotion_hint)
wants_promotion_hints_set_.insert(id);
#endif // OS_ANDROID
}
int DisplayResourceProvider::CreateChild(
const ReturnCallback& return_callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Child child_info;
child_info.return_callback = return_callback;
int child = next_child_++;
children_[child] = child_info;
return child;
}
void DisplayResourceProvider::SetChildNeedsSyncTokens(int child_id,
bool needs) {
auto it = children_.find(child_id);
DCHECK(it != children_.end());
it->second.needs_sync_tokens = needs;
}
void DisplayResourceProvider::DestroyChild(int child_id) {
auto it = children_.find(child_id);
DCHECK(it != children_.end());
DestroyChildInternal(it, NORMAL);
}
void DisplayResourceProvider::ReceiveFromChild(
int child_id,
const std::vector<TransferableResource>& resources) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Child& child_info = children_.find(child_id)->second;
DCHECK(!child_info.marked_for_deletion);
for (std::vector<TransferableResource>::const_iterator it = resources.begin();
it != resources.end(); ++it) {
auto resource_in_map_it = child_info.child_to_parent_map.find(it->id);
if (resource_in_map_it != child_info.child_to_parent_map.end()) {
ChildResource* resource = GetResource(resource_in_map_it->second);
resource->marked_for_deletion = false;
resource->imported_count++;
continue;
}
if (it->is_software != IsSoftware() ||
it->mailbox_holder.mailbox.IsZero()) {
TRACE_EVENT0(
"viz", "DisplayResourceProvider::ReceiveFromChild dropping invalid");
std::vector<ReturnedResource> to_return;
to_return.push_back(it->ToReturnedResource());
child_info.return_callback.Run(to_return);
continue;
}
ResourceId local_id = next_id_++;
if (it->is_software) {
DCHECK(IsBitmapFormatSupported(it->format));
InsertResource(local_id, ChildResource(child_id, *it));
} else {
InsertResource(local_id, ChildResource(child_id, *it));
}
child_info.child_to_parent_map[it->id] = local_id;
}
}
void DisplayResourceProvider::DeclareUsedResourcesFromChild(
int child,
const ResourceIdSet& resources_from_child) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto child_it = children_.find(child);
DCHECK(child_it != children_.end());
Child& child_info = child_it->second;
DCHECK(!child_info.marked_for_deletion);
std::vector<ResourceId> unused;
for (auto it = child_info.child_to_parent_map.begin();
it != child_info.child_to_parent_map.end(); ++it) {
ResourceId local_id = it->second;
bool resource_is_in_use = resources_from_child.count(it->first) > 0;
if (!resource_is_in_use)
unused.push_back(local_id);
}
DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
}
const std::unordered_map<ResourceId, ResourceId>&
DisplayResourceProvider::GetChildToParentMap(int child) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ChildMap::const_iterator it = children_.find(child);
DCHECK(it != children_.end());
DCHECK(!it->second.marked_for_deletion);
return it->second.child_to_parent_map;
}
bool DisplayResourceProvider::InUse(ResourceId id) {
ChildResource* resource = GetResource(id);
return resource->lock_for_read_count > 0 || resource->locked_for_external_use;
}
DisplayResourceProvider::ChildResource* DisplayResourceProvider::InsertResource(
ResourceId id,
ChildResource resource) {
auto result =
resources_.insert(ResourceMap::value_type(id, std::move(resource)));
DCHECK(result.second);
return &result.first->second;
}
DisplayResourceProvider::ChildResource* DisplayResourceProvider::GetResource(
ResourceId id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(id);
auto it = resources_.find(id);
DCHECK(it != resources_.end());
return &it->second;
}
DisplayResourceProvider::ChildResource* DisplayResourceProvider::TryGetResource(
ResourceId id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!id)
return nullptr;
auto it = resources_.find(id);
if (it == resources_.end())
return nullptr;
return &it->second;
}
void DisplayResourceProvider::PopulateSkBitmapWithResource(
SkBitmap* sk_bitmap,
const ChildResource* resource) {
DCHECK(IsBitmapFormatSupported(resource->transferable.format));
SkImageInfo info =
SkImageInfo::MakeN32Premul(resource->transferable.size.width(),
resource->transferable.size.height());
bool pixels_installed = sk_bitmap->installPixels(
info, resource->shared_bitmap->pixels(), info.minRowBytes());
DCHECK(pixels_installed);
}
void DisplayResourceProvider::DeleteResourceInternal(ResourceMap::iterator it,
DeleteStyle style) {
TRACE_EVENT0("viz", "DosplayResourceProvider::DeleteResourceInternal");
ChildResource* resource = &it->second;
if (resource->gl_id) {
GLES2Interface* gl = ContextGL();
DCHECK(gl);
gl->DeleteTextures(1, &resource->gl_id);
}
resources_.erase(it);
}
void DisplayResourceProvider::WaitSyncTokenInternal(ChildResource* resource) {
DCHECK(resource);
if (!resource->ShouldWaitSyncToken())
return;
GLES2Interface* gl = ContextGL();
DCHECK(gl);
// In the case of context lost, this sync token may be empty (see comment in
// the UpdateSyncToken() function). The WaitSyncTokenCHROMIUM() function
// handles empty sync tokens properly so just wait anyways and update the
// state the synchronized.
gl->WaitSyncTokenCHROMIUM(resource->sync_token().GetConstData());
resource->SetSynchronized();
}
GLES2Interface* DisplayResourceProvider::ContextGL() const {
ContextProvider* context_provider = compositor_context_provider_;
return context_provider ? context_provider->ContextGL() : nullptr;
}
const DisplayResourceProvider::ChildResource*
DisplayResourceProvider::LockForRead(ResourceId id) {
// TODO(ericrk): We should never fail TryGetResource, but we appear to be
// doing so on Android in rare cases. Handle this gracefully until a better
// solution can be found. https://crbug.com/811858
ChildResource* resource = TryGetResource(id);
if (!resource)
return nullptr;
// Mailbox sync_tokens must be processed by a call to WaitSyncToken() prior to
// calling LockForRead().
DCHECK_NE(NEEDS_WAIT, resource->synchronization_state());
if (resource->is_gpu_resource_type() && !resource->gl_id) {
GLES2Interface* gl = ContextGL();
DCHECK(gl);
resource->gl_id = gl->CreateAndConsumeTextureCHROMIUM(
resource->transferable.mailbox_holder.mailbox.name);
resource->SetLocallyUsed();
}
if (!resource->shared_bitmap && !resource->is_gpu_resource_type()) {
const SharedBitmapId& shared_bitmap_id =
resource->transferable.mailbox_holder.mailbox;
std::unique_ptr<SharedBitmap> bitmap =
shared_bitmap_manager_->GetSharedBitmapFromId(
resource->transferable.size, resource->transferable.format,
shared_bitmap_id);
if (bitmap) {
resource->shared_bitmap = std::move(bitmap);
resource->shared_bitmap_tracing_guid =
shared_bitmap_manager_->GetSharedBitmapTracingGUIDFromId(
shared_bitmap_id);
}
}
resource->lock_for_read_count++;
if (resource->transferable.read_lock_fences_enabled) {
if (current_read_lock_fence_.get())
current_read_lock_fence_->Set();
resource->read_lock_fence = current_read_lock_fence_;
}
return resource;
}
void DisplayResourceProvider::UnlockForRead(ResourceId id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto it = resources_.find(id);
// TODO(ericrk): We should never fail to find id, but we appear to be
// doing so on Android in rare cases. Handle this gracefully until a better
// solution can be found. https://crbug.com/811858
if (it == resources_.end())
return;
ChildResource* resource = &it->second;
DCHECK_GT(resource->lock_for_read_count, 0);
resource->lock_for_read_count--;
TryReleaseResource(it);
}
ResourceMetadata DisplayResourceProvider::LockForExternalUse(ResourceId id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto it = resources_.find(id);
DCHECK(it != resources_.end());
ChildResource* resource = &it->second;
ResourceMetadata metadata;
// Make sure there is no outstanding LockForExternalUse without calling
// UnlockForExternalUse.
DCHECK(!resource->locked_for_external_use);
// TODO(penghuang): support software resource.
DCHECK(resource->is_gpu_resource_type());
metadata.mailbox = resource->transferable.mailbox_holder.mailbox;
metadata.backend_format = GrBackendFormat::MakeGL(
TextureStorageFormat(resource->transferable.format),
resource->transferable.mailbox_holder.texture_target);
metadata.size = resource->transferable.size;
metadata.mip_mapped = GrMipMapped::kNo;
metadata.origin = kTopLeft_GrSurfaceOrigin;
metadata.color_type = ResourceFormatToClosestSkColorType(
!IsSoftware(), resource->transferable.format);
metadata.alpha_type = kPremul_SkAlphaType;
metadata.color_space = nullptr;
metadata.sync_token = resource->sync_token();
resource->locked_for_external_use = true;
return metadata;
}
void DisplayResourceProvider::UnlockForExternalUse(
ResourceId id,
const gpu::SyncToken& sync_token) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto it = resources_.find(id);
DCHECK(it != resources_.end());
DCHECK(sync_token.verified_flush());
ChildResource* resource = &it->second;
DCHECK(resource->locked_for_external_use);
// TODO(penghuang): support software resource.
DCHECK(resource->is_gpu_resource_type());
// Update the resource sync token to |sync_token|. When the next frame is
// being composited, the DeclareUsedResourcesFromChild() will be called with
// resources belong to every child for the next frame. If the resource is not
// used by the next frame, the resource will be returned to a child which
// owns it with the |sync_token|. The child is responsible for issuing a
// WaitSyncToken GL command with the |sync_token| before reusing it.
resource->UpdateSyncToken(sync_token);
resource->locked_for_external_use = false;
TryReleaseResource(it);
}
void DisplayResourceProvider::TryReleaseResource(ResourceMap::iterator it) {
ResourceId id = it->first;
ChildResource* resource = &it->second;
if (resource->marked_for_deletion && !resource->lock_for_read_count &&
!resource->locked_for_external_use) {
if (!resource->child_id) {
// The resource belongs to this instance, so it can be destroyed.
// TODO(danakj): Is this dead code?
#if defined(OS_ANDROID)
DeletePromotionHint(it, NORMAL);
#endif
DeleteResourceInternal(it, NORMAL);
} else {
if (batch_return_resources_) {
batched_returning_resources_[resource->child_id].push_back(id);
} else {
auto child_it = children_.find(resource->child_id);
std::vector<ResourceId> unused;
unused.push_back(id);
DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
}
}
}
}
GLenum DisplayResourceProvider::BindForSampling(ResourceId resource_id,
GLenum unit,
GLenum filter) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
GLES2Interface* gl = ContextGL();
auto it = resources_.find(resource_id);
// TODO(ericrk): We should never fail to find resource_id, but we appear to
// be doing so on Android in rare cases. Handle this gracefully until a
// better solution can be found. https://crbug.com/811858
if (it == resources_.end())
return GL_TEXTURE_2D;
ChildResource* resource = &it->second;
DCHECK(resource->lock_for_read_count);
ScopedSetActiveTexture scoped_active_tex(gl, unit);
GLenum target = resource->transferable.mailbox_holder.texture_target;
gl->BindTexture(target, resource->gl_id);
if (filter != resource->filter) {
gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
resource->filter = filter;
}
return target;
}
bool DisplayResourceProvider::ReadLockFenceHasPassed(
const ChildResource* resource) {
return !resource->read_lock_fence || resource->read_lock_fence->HasPassed();
}
#if defined(OS_ANDROID)
void DisplayResourceProvider::DeletePromotionHint(ResourceMap::iterator it,
DeleteStyle style) {
ChildResource* resource = &it->second;
// If this resource was interested in promotion hints, then remove it from
// the set of resources that we'll notify.
if (resource->transferable.wants_promotion_hint)
wants_promotion_hints_set_.erase(it->first);
}
#endif
void DisplayResourceProvider::DeleteAndReturnUnusedResourcesToChild(
ChildMap::iterator child_it,
DeleteStyle style,
const std::vector<ResourceId>& unused) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(child_it != children_.end());
Child* child_info = &child_it->second;
if (unused.empty() && !child_info->marked_for_deletion)
return;
std::vector<ReturnedResource> to_return;
to_return.reserve(unused.size());
std::vector<ReturnedResource*> need_synchronization_resources;
std::vector<GLbyte*> unverified_sync_tokens;
std::vector<size_t> to_return_indices_unverified;
GLES2Interface* gl = ContextGL();
for (ResourceId local_id : unused) {
auto it = resources_.find(local_id);
CHECK(it != resources_.end());
ChildResource& resource = it->second;
ResourceId child_id = resource.transferable.id;
DCHECK(child_info->child_to_parent_map.count(child_id));
bool is_lost = (resource.is_gpu_resource_type() && lost_context_provider_);
if (resource.lock_for_read_count > 0 || resource.locked_for_external_use) {
if (style != FOR_SHUTDOWN) {
// Defer this resource deletion.
resource.marked_for_deletion = true;
continue;
}
// We can't postpone the deletion, so we'll have to lose it.
is_lost = true;
} else if (!ReadLockFenceHasPassed(&resource)) {
// TODO(dcastagna): see if it's possible to use this logic for
// the branch above too, where the resource is locked or still exported.
if (style != FOR_SHUTDOWN && !child_info->marked_for_deletion) {
// Defer this resource deletion.
resource.marked_for_deletion = true;
continue;
}
// We can't postpone the deletion, so we'll have to lose it.
is_lost = true;
}
if (resource.is_gpu_resource_type() &&
resource.filter != resource.transferable.filter) {
DCHECK(resource.transferable.mailbox_holder.texture_target);
DCHECK(resource.gl_id);
DCHECK(gl);
gl->BindTexture(resource.transferable.mailbox_holder.texture_target,
resource.gl_id);
gl->TexParameteri(resource.transferable.mailbox_holder.texture_target,
GL_TEXTURE_MIN_FILTER, resource.transferable.filter);
gl->TexParameteri(resource.transferable.mailbox_holder.texture_target,
GL_TEXTURE_MAG_FILTER, resource.transferable.filter);
resource.SetLocallyUsed();
}
ReturnedResource returned;
returned.id = child_id;
returned.sync_token = resource.sync_token();
returned.count = resource.imported_count;
returned.lost = is_lost;
to_return.push_back(returned);
if (resource.is_gpu_resource_type() && child_info->needs_sync_tokens) {
if (resource.needs_sync_token()) {
need_synchronization_resources.push_back(&to_return.back());
} else if (returned.sync_token.HasData() &&
!returned.sync_token.verified_flush()) {
// Before returning any sync tokens, they must be verified. Store an
// index into |to_return| instead of a pointer as vectors may realloc
// and move their data.
to_return_indices_unverified.push_back(to_return.size() - 1);
}
}
child_info->child_to_parent_map.erase(child_id);
resource.imported_count = 0;
#if defined(OS_ANDROID)
DeletePromotionHint(it, style);
#endif
DeleteResourceInternal(it, style);
}
for (size_t i : to_return_indices_unverified)
unverified_sync_tokens.push_back(to_return[i].sync_token.GetData());
gpu::SyncToken new_sync_token;
if (!need_synchronization_resources.empty()) {
DCHECK(child_info->needs_sync_tokens);
DCHECK(gl);
gl->GenUnverifiedSyncTokenCHROMIUM(new_sync_token.GetData());
unverified_sync_tokens.push_back(new_sync_token.GetData());
}
if (!unverified_sync_tokens.empty()) {
DCHECK(child_info->needs_sync_tokens);
DCHECK(gl);
gl->VerifySyncTokensCHROMIUM(unverified_sync_tokens.data(),
unverified_sync_tokens.size());
}
// Set sync token after verification.
for (ReturnedResource* returned : need_synchronization_resources)
returned->sync_token = new_sync_token;
if (!to_return.empty())
child_info->return_callback.Run(to_return);
if (child_info->marked_for_deletion &&
child_info->child_to_parent_map.empty()) {
children_.erase(child_it);
}
}
void DisplayResourceProvider::DestroyChildInternal(ChildMap::iterator it,
DeleteStyle style) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Child& child = it->second;
DCHECK(style == FOR_SHUTDOWN || !child.marked_for_deletion);
std::vector<ResourceId> resources_for_child;
for (auto child_it = child.child_to_parent_map.begin();
child_it != child.child_to_parent_map.end(); ++child_it) {
ResourceId id = child_it->second;
resources_for_child.push_back(id);
}
child.marked_for_deletion = true;
DeleteAndReturnUnusedResourcesToChild(it, style, resources_for_child);
}
void DisplayResourceProvider::SetBatchReturnResources(bool batch) {
DCHECK_NE(batch_return_resources_, batch);
batch_return_resources_ = batch;
if (!batch) {
for (const auto& resources : batched_returning_resources_) {
auto child_it = children_.find(resources.first);
DCHECK(child_it != children_.end());
DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, resources.second);
}
batched_returning_resources_.clear();
}
}
DisplayResourceProvider::ScopedReadLockGL::ScopedReadLockGL(
DisplayResourceProvider* resource_provider,
ResourceId resource_id)
: resource_provider_(resource_provider), resource_id_(resource_id) {
const ChildResource* resource = resource_provider->LockForRead(resource_id);
// TODO(ericrk): We should never fail LockForRead, but we appear to be
// doing so on Android in rare cases. Handle this gracefully until a better
// solution can be found. https://crbug.com/811858
if (!resource)
return;
texture_id_ = resource->gl_id;
target_ = resource->transferable.mailbox_holder.texture_target;
size_ = resource->transferable.size;
color_space_ = resource->transferable.color_space;
}
DisplayResourceProvider::ScopedReadLockGL::~ScopedReadLockGL() {
resource_provider_->UnlockForRead(resource_id_);
}
DisplayResourceProvider::ScopedSamplerGL::ScopedSamplerGL(
DisplayResourceProvider* resource_provider,
ResourceId resource_id,
GLenum filter)
: resource_lock_(resource_provider, resource_id),
unit_(GL_TEXTURE0),
target_(resource_provider->BindForSampling(resource_id, unit_, filter)) {}
DisplayResourceProvider::ScopedSamplerGL::ScopedSamplerGL(
DisplayResourceProvider* resource_provider,
ResourceId resource_id,
GLenum unit,
GLenum filter)
: resource_lock_(resource_provider, resource_id),
unit_(unit),
target_(resource_provider->BindForSampling(resource_id, unit_, filter)) {}
DisplayResourceProvider::ScopedSamplerGL::~ScopedSamplerGL() = default;
DisplayResourceProvider::ScopedReadLockSkImage::ScopedReadLockSkImage(
DisplayResourceProvider* resource_provider,
ResourceId resource_id)
: resource_provider_(resource_provider), resource_id_(resource_id) {
const ChildResource* resource = resource_provider->LockForRead(resource_id);
DCHECK(resource);
// Use cached SkImage if possible.
auto it = resource_provider_->resource_sk_image_.find(resource_id);
if (it != resource_provider_->resource_sk_image_.end()) {
sk_image_ = it->second;
return;
}
if (resource->is_gpu_resource_type()) {
DCHECK(resource->gl_id);
GrGLTextureInfo texture_info;
texture_info.fID = resource->gl_id;
texture_info.fTarget = resource->transferable.mailbox_holder.texture_target;
texture_info.fFormat = TextureStorageFormat(resource->transferable.format);
GrBackendTexture backend_texture(resource->transferable.size.width(),
resource->transferable.size.height(),
GrMipMapped::kNo, texture_info);
sk_image_ = SkImage::MakeFromTexture(
resource_provider->compositor_context_provider_->GrContext(),
backend_texture, kTopLeft_GrSurfaceOrigin,
ResourceFormatToClosestSkColorType(!resource_provider->IsSoftware(),
resource->transferable.format),
kPremul_SkAlphaType, nullptr);
return;
}
if (!resource->shared_bitmap) {
// If a CompositorFrameSink is destroyed, it destroys all SharedBitmapIds
// that it registered. In this case, a CompositorFrame can be drawn with
// SharedBitmapIds that are not known in the viz service. As well, a
// misbehaved client can use SharedBitampIds that it did not report to
// the service. Then the |shared_bitmap| will be null, and this read lock
// will not be valid. Software-compositing users of this read lock must
// check for valid() to deal with this scenario.
sk_image_ = nullptr;
return;
}
SkBitmap sk_bitmap;
resource_provider->PopulateSkBitmapWithResource(&sk_bitmap, resource);
sk_bitmap.setImmutable();
sk_image_ = SkImage::MakeFromBitmap(sk_bitmap);
}
DisplayResourceProvider::ScopedReadLockSkImage::~ScopedReadLockSkImage() {
resource_provider_->UnlockForRead(resource_id_);
}
DisplayResourceProvider::LockSetForExternalUse::LockSetForExternalUse(
DisplayResourceProvider* resource_provider)
: resource_provider_(resource_provider) {}
DisplayResourceProvider::LockSetForExternalUse::~LockSetForExternalUse() {
DCHECK(resources_.empty());
}
ResourceMetadata DisplayResourceProvider::LockSetForExternalUse::LockResource(
ResourceId id) {
DCHECK(std::find(resources_.begin(), resources_.end(), id) ==
resources_.end());
resources_.push_back(id);
return resource_provider_->LockForExternalUse(id);
}
void DisplayResourceProvider::LockSetForExternalUse::UnlockResources(
const gpu::SyncToken& sync_token) {
for (const auto& id : resources_)
resource_provider_->UnlockForExternalUse(id, sync_token);
resources_.clear();
}
DisplayResourceProvider::SynchronousFence::SynchronousFence(
gpu::gles2::GLES2Interface* gl)
: gl_(gl), has_synchronized_(true) {}
DisplayResourceProvider::SynchronousFence::~SynchronousFence() = default;
void DisplayResourceProvider::SynchronousFence::Set() {
has_synchronized_ = false;
}
bool DisplayResourceProvider::SynchronousFence::HasPassed() {
if (!has_synchronized_) {
has_synchronized_ = true;
Synchronize();
}
return true;
}
void DisplayResourceProvider::SynchronousFence::Wait() {
HasPassed();
}
void DisplayResourceProvider::SynchronousFence::Synchronize() {
TRACE_EVENT0("viz", "DisplayResourceProvider::SynchronousFence::Synchronize");
gl_->Finish();
}
DisplayResourceProvider::ScopedBatchReturnResources::ScopedBatchReturnResources(
DisplayResourceProvider* resource_provider)
: resource_provider_(resource_provider) {
resource_provider_->SetBatchReturnResources(true);
}
DisplayResourceProvider::ScopedBatchReturnResources::
~ScopedBatchReturnResources() {
resource_provider_->SetBatchReturnResources(false);
}
DisplayResourceProvider::Child::Child() = default;
DisplayResourceProvider::Child::Child(const Child& other) = default;
DisplayResourceProvider::Child::~Child() = default;
DisplayResourceProvider::ChildResource::ChildResource(
int child_id,
const TransferableResource& transferable)
: child_id(child_id),
transferable(transferable),
filter(transferable.filter) {
if (is_gpu_resource_type())
UpdateSyncToken(transferable.mailbox_holder.sync_token);
else
SetSynchronized();
}
DisplayResourceProvider::ChildResource::ChildResource(ChildResource&& other) =
default;
DisplayResourceProvider::ChildResource::~ChildResource() = default;
void DisplayResourceProvider::ChildResource::SetLocallyUsed() {
synchronization_state_ = LOCALLY_USED;
sync_token_.Clear();
}
void DisplayResourceProvider::ChildResource::SetSynchronized() {
synchronization_state_ = SYNCHRONIZED;
}
void DisplayResourceProvider::ChildResource::UpdateSyncToken(
const gpu::SyncToken& sync_token) {
DCHECK(is_gpu_resource_type());
// An empty sync token may be used if commands are guaranteed to have run on
// the gpu process or in case of context loss.
sync_token_ = sync_token;
synchronization_state_ = sync_token.HasData() ? NEEDS_WAIT : SYNCHRONIZED;
}
} // namespace viz