Chromium Code Reviews| Index: cc/surfaces/surface_manager.cc |
| diff --git a/cc/surfaces/surface_manager.cc b/cc/surfaces/surface_manager.cc |
| index 0e204a44ed81e61b84010ce0e4d524004cea0d6c..8dd1dbe24a8fcaf70745e8fa22664f18949c099f 100644 |
| --- a/cc/surfaces/surface_manager.cc |
| +++ b/cc/surfaces/surface_manager.cc |
| @@ -7,12 +7,23 @@ |
| #include <stddef.h> |
| #include <stdint.h> |
| +#include <utility> |
| + |
| #include "base/logging.h" |
| #include "cc/surfaces/surface.h" |
| #include "cc/surfaces/surface_factory_client.h" |
| #include "cc/surfaces/surface_id_allocator.h" |
| namespace cc { |
| +namespace { |
| + |
| +// Copies contents of |set| so we can iterate over them while removing entries |
| +// from |set| without worrying about iterator invalidation. |
| +std::vector<SurfaceId> CopySurfaceIds(const SurfaceManager::SurfaceIdSet& set) { |
| + return std::vector<SurfaceId>(set.begin(), set.end()); |
| +} |
| + |
| +} // namespace |
| SurfaceManager::FrameSinkSourceMapping::FrameSinkSourceMapping() |
| : client(nullptr), source(nullptr) {} |
| @@ -56,6 +67,8 @@ void SurfaceManager::DeregisterSurface(const SurfaceId& surface_id) { |
| SurfaceMap::iterator it = surface_map_.find(surface_id); |
| DCHECK(it != surface_map_.end()); |
| surface_map_.erase(it); |
| + child_to_parent_refs_.erase(surface_id); |
| + parent_to_child_refs_.erase(surface_id); |
| } |
| void SurfaceManager::Destroy(std::unique_ptr<Surface> surface) { |
| @@ -68,10 +81,8 @@ void SurfaceManager::Destroy(std::unique_ptr<Surface> surface) { |
| void SurfaceManager::DidSatisfySequences(const FrameSinkId& frame_sink_id, |
| std::vector<uint32_t>* sequence) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| - for (std::vector<uint32_t>::iterator it = sequence->begin(); |
| - it != sequence->end(); |
| - ++it) { |
| - satisfied_sequences_.insert(SurfaceSequence(frame_sink_id, *it)); |
| + for (uint32_t value : *sequence) { |
| + satisfied_sequences_.insert(SurfaceSequence(frame_sink_id, value)); |
| } |
| sequence->clear(); |
| GarbageCollectSurfaces(); |
| @@ -87,22 +98,139 @@ void SurfaceManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) { |
| GarbageCollectSurfaces(); |
| } |
| +void SurfaceManager::AddSurfaceIdReference(const SurfaceId& parent_id, |
| + const SurfaceId& child_id) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + // This should never happen but we don't want to DHCECK if we get bad input |
| + // from a compromised client. |
| + if (parent_id == child_id) { |
| + LOG(ERROR) << "Cannot add self reference for " << parent_id.ToString(); |
| + return; |
| + } |
| + |
| + parent_to_child_refs_[parent_id].insert(child_id); |
| + child_to_parent_refs_[child_id].insert(parent_id); |
| + |
| + DCHECK_EQ(parent_to_child_refs_[parent_id].count(child_id), 1u); |
| + DCHECK_EQ(child_to_parent_refs_[child_id].count(parent_id), 1u); |
| +} |
| + |
| +void SurfaceManager::RemoveSurfaceIdReference(const SurfaceId& parent_id, |
| + const SurfaceId& child_id, |
| + bool should_run_gc) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + // If the GPU process crashes a client could try to free an invalid surface. |
| + if (parent_to_child_refs_.count(parent_id) == 0 || |
| + parent_to_child_refs_[parent_id].count(child_id) == 0) { |
| + LOG(ERROR) << "No reference from " << parent_id.ToString() << " to " |
| + << child_id.ToString(); |
| + return; |
| + } |
| + |
| + // Remove the reference from parent to child. This doesn't change anything |
| + // about the validity of the parent. |
| + parent_to_child_refs_[parent_id].erase(child_id); |
| + |
| + // Remove the reference from child to parent. This might drop the number of |
| + // references to the child to zero. |
| + DCHECK_EQ(child_to_parent_refs_.count(child_id), 1u); |
| + SurfaceIdSet& child_refs = child_to_parent_refs_[child_id]; |
| + DCHECK_EQ(child_refs.count(parent_id), 1u); |
| + child_refs.erase(parent_id); |
| + |
| + if (!child_refs.empty()) |
| + return; |
| + |
| + // Remove any references the child holds before it gets garbage collected. |
| + for (auto& surface_id : CopySurfaceIds(parent_to_child_refs_[child_id])) |
| + RemoveSurfaceIdReference(child_id, surface_id, false); |
| + DCHECK_EQ(GetSurfaceRefereeCount(child_id), 0u); |
| + |
| + if (should_run_gc) |
|
Fady Samuel
2016/11/02 03:10:36
nit: add a comment as to when you'd run gc?
|
| + GarbageCollectSurfaces(); |
| +} |
| + |
| +void SurfaceManager::RemoveAllReferencesForFrameSink( |
| + const FrameSinkId& frame_sink_id) { |
| + // Find set of surfaces that belong to the FrameSink. |
| + SurfaceIdSet frame_sink_surfaces; |
| + for (auto& map_entry : parent_to_child_refs_) { |
| + const SurfaceId& surface_id = map_entry.first; |
| + if (surface_id.frame_sink_id() == frame_sink_id) |
| + frame_sink_surfaces.insert(surface_id); |
| + } |
| + for (auto& map_entry : child_to_parent_refs_) { |
| + const SurfaceId& surface_id = map_entry.first; |
| + if (surface_id.frame_sink_id() == frame_sink_id) |
| + frame_sink_surfaces.insert(surface_id); |
| + } |
| + |
| + // Clear all surface references belonging to FrameSink |frame_sink_id|. |
| + for (const SurfaceId& surface_id : frame_sink_surfaces) |
| + RemoveAllReferencesForSurfaceId(surface_id); |
| +} |
| + |
| +void SurfaceManager::RemoveAllReferencesForSurfaceId( |
| + const SurfaceId& surface_id) { |
| + // Remove any references from parents to this surface, since this surface |
| + // no longer exists. This surface will have zero references when finished. |
| + if (child_to_parent_refs_.count(surface_id) > 0) { |
| + for (auto& parent_id : CopySurfaceIds(child_to_parent_refs_[surface_id])) |
| + RemoveSurfaceIdReference(parent_id, surface_id); |
| + } |
| + DCHECK_EQ(GetSurfaceReferenceCount(surface_id), 0u); |
| + |
| + // Remove any references from this surface to children, since this surface |
| + // no longer exists. A child surface may drop to zero references here, |
| + // recursively clearing any references the child surface holds. |
| + if (parent_to_child_refs_.count(surface_id) > 0) { |
| + for (auto& child_id : CopySurfaceIds(parent_to_child_refs_[surface_id])) |
| + RemoveSurfaceIdReference(surface_id, child_id); |
| + } |
| + DCHECK_EQ(GetSurfaceRefereeCount(surface_id), 0u); |
| +} |
| + |
| +size_t SurfaceManager::GetSurfaceReferenceCount( |
| + const SurfaceId& surface_id) const { |
| + auto iter = child_to_parent_refs_.find(surface_id); |
| + if (iter == child_to_parent_refs_.end()) |
| + return 0; |
| + return iter->second.size(); |
| +} |
| + |
| +size_t SurfaceManager::GetSurfaceRefereeCount( |
| + const SurfaceId& surface_id) const { |
| + auto iter = parent_to_child_refs_.find(surface_id); |
| + if (iter == parent_to_child_refs_.end()) |
| + return 0; |
| + return iter->second.size(); |
| +} |
| + |
| void SurfaceManager::GarbageCollectSurfaces() { |
| // Simple mark and sweep GC. |
| // TODO(jbauman): Reduce the amount of work when nothing needs to be |
| // destroyed. |
| std::vector<SurfaceId> live_surfaces; |
| - std::set<SurfaceId> live_surfaces_set; |
| + std::unordered_set<SurfaceId, SurfaceIdHash> live_surfaces_set; |
| // GC roots are surfaces that have not been destroyed, or have not had all |
| // their destruction dependencies satisfied. |
| for (auto& map_entry : surface_map_) { |
| - map_entry.second->SatisfyDestructionDependencies(&satisfied_sequences_, |
| - &valid_frame_sink_ids_); |
| - if (!map_entry.second->destroyed() || |
| - map_entry.second->GetDestructionDependencyCount()) { |
| - live_surfaces_set.insert(map_entry.first); |
| - live_surfaces.push_back(map_entry.first); |
| + const SurfaceId& surface_id = map_entry.first; |
| + Surface* surface = map_entry.second; |
| + surface->SatisfyDestructionDependencies(&satisfied_sequences_, |
| + &valid_frame_sink_ids_); |
| + |
| + // Never use both SurfaceSequence and Surface references. |
| + DCHECK(surface->GetDestructionDependencyCount() == 0 || |
| + GetSurfaceReferenceCount(surface_id) == 0); |
| + |
| + if (!surface->destroyed() || surface->GetDestructionDependencyCount() > 0 || |
| + GetSurfaceReferenceCount(surface_id) > 0) { |
| + live_surfaces_set.insert(surface_id); |
| + live_surfaces.push_back(surface_id); |
| } |
| } |
| @@ -127,15 +255,16 @@ void SurfaceManager::GarbageCollectSurfaces() { |
| std::vector<std::unique_ptr<Surface>> to_destroy; |
| // Destroy all remaining unreachable surfaces. |
| - for (SurfaceDestroyList::iterator dest_it = surfaces_to_destroy_.begin(); |
| - dest_it != surfaces_to_destroy_.end();) { |
| - if (!live_surfaces_set.count((*dest_it)->surface_id())) { |
| - std::unique_ptr<Surface> surf(std::move(*dest_it)); |
| - DeregisterSurface(surf->surface_id()); |
| - dest_it = surfaces_to_destroy_.erase(dest_it); |
| - to_destroy.push_back(std::move(surf)); |
| + for (auto iter = surfaces_to_destroy_.begin(); |
| + iter != surfaces_to_destroy_.end();) { |
| + SurfaceId surface_id = (*iter)->surface_id(); |
| + if (!live_surfaces_set.count(surface_id)) { |
| + DeregisterSurface(surface_id); |
| + to_destroy.push_back(std::move(*iter)); |
| + iter = surfaces_to_destroy_.erase(iter); |
| + LOG(ERROR) << "GC destroyed " << surface_id.ToString(); |
| } else { |
| - ++dest_it; |
| + ++iter; |
| } |
| } |
| @@ -146,21 +275,17 @@ void SurfaceManager::RegisterSurfaceFactoryClient( |
| const FrameSinkId& frame_sink_id, |
| SurfaceFactoryClient* client) { |
| DCHECK(client); |
| - DCHECK(!frame_sink_source_map_[frame_sink_id].client); |
| DCHECK_EQ(valid_frame_sink_ids_.count(frame_sink_id), 1u); |
| - auto iter = frame_sink_source_map_.find(frame_sink_id); |
| - if (iter == frame_sink_source_map_.end()) { |
| - auto insert_result = frame_sink_source_map_.insert( |
| - std::make_pair(frame_sink_id, FrameSinkSourceMapping())); |
| - DCHECK(insert_result.second); |
| - iter = insert_result.first; |
| - } |
| - iter->second.client = client; |
| + // Will create a new FrameSinkSourceMapping for |frame_sink_id| if necessary. |
| + FrameSinkSourceMapping& frame_sink_source = |
| + frame_sink_source_map_[frame_sink_id]; |
| + DCHECK(!frame_sink_source.client); |
| + frame_sink_source.client = client; |
| // Propagate any previously set sources to the new client. |
| - if (iter->second.source) |
| - client->SetBeginFrameSource(iter->second.source); |
| + if (frame_sink_source.source) |
| + client->SetBeginFrameSource(frame_sink_source.source); |
| } |
| void SurfaceManager::UnregisterSurfaceFactoryClient( |
| @@ -168,6 +293,8 @@ void SurfaceManager::UnregisterSurfaceFactoryClient( |
| DCHECK_EQ(valid_frame_sink_ids_.count(frame_sink_id), 1u); |
| DCHECK_EQ(frame_sink_source_map_.count(frame_sink_id), 1u); |
| + RemoveAllReferencesForFrameSink(frame_sink_id); |
| + |
| auto iter = frame_sink_source_map_.find(frame_sink_id); |
| if (iter->second.source) |
| iter->second.client->SetBeginFrameSource(nullptr); |
| @@ -338,7 +465,7 @@ Surface* SurfaceManager::GetSurfaceForId(const SurfaceId& surface_id) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| SurfaceMap::iterator it = surface_map_.find(surface_id); |
| if (it == surface_map_.end()) |
| - return NULL; |
| + return nullptr; |
| return it->second; |
| } |