Chromium Code Reviews| Index: cc/resources/resource_provider.cc |
| diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc |
| index 017c2e9cacebe3501bf24af8e4552d7086f12a97..6e7beff473fc306dc17287937b335c2eb4d76684 100644 |
| --- a/cc/resources/resource_provider.cc |
| +++ b/cc/resources/resource_provider.cc |
| @@ -205,6 +205,8 @@ scoped_ptr<ResourceProvider> ResourceProvider::Create( |
| } |
| ResourceProvider::~ResourceProvider() { |
| + while (!children_.empty()) |
| + DestroyChild(children_.begin()->first); |
| while (!resources_.empty()) |
| DeleteResourceInternal(resources_.begin(), ForShutdown); |
| @@ -779,9 +781,12 @@ void ResourceProvider::CleanUpGLIfNeeded() { |
| Finish(); |
| } |
| -int ResourceProvider::CreateChild() { |
| +int ResourceProvider::CreateChild(const ReturnCallback& return_callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| Child child_info; |
| + child_info.return_callback = return_callback; |
| + |
| int child = next_child_++; |
| children_[child] = child_info; |
| return child; |
| @@ -789,20 +794,26 @@ int ResourceProvider::CreateChild() { |
| void ResourceProvider::DestroyChild(int child_id) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| ChildMap::iterator it = children_.find(child_id); |
| DCHECK(it != children_.end()); |
| Child& child = it->second; |
| + |
| + ResourceIdArray resources_for_child; |
| + |
| for (ResourceIdMap::iterator child_it = child.child_to_parent_map.begin(); |
| child_it != child.child_to_parent_map.end(); |
| ++child_it) { |
| ResourceId id = child_it->second; |
| - // We're abandoning this resource, it will not get recycled. |
| - // crbug.com/224062 |
| - ResourceMap::iterator resource_it = resources_.find(id); |
| - CHECK(resource_it != resources_.end()); |
| - resource_it->second.imported_count = 0; |
| - DeleteResource(id); |
| + resources_for_child.push_back(id); |
| } |
| + |
| + // If the child is going away, don't consider any resources in use. |
| + child.in_use_resources.clear(); |
| + |
| + DeleteAndReturnUnusedResourcesToChild( |
| + &child, ForShutdown, resources_for_child); |
| + |
| children_.erase(it); |
| } |
| @@ -844,62 +855,6 @@ void ResourceProvider::PrepareSendToParent(const ResourceIdArray& resources, |
| } |
| } |
| -void ResourceProvider::PrepareSendReturnsToChild( |
| - int child, |
| - const ResourceIdArray& resources, |
| - ReturnedResourceArray* list) { |
| - DCHECK(thread_checker_.CalledOnValidThread()); |
| - WebGraphicsContext3D* context3d = Context3d(); |
| - if (!context3d || !context3d->makeContextCurrent()) { |
| - // TODO(skaslev): Implement this path for software compositing. |
| - return; |
| - } |
| - Child& child_info = children_.find(child)->second; |
| - bool need_sync_point = false; |
| - for (ResourceIdArray::const_iterator it = resources.begin(); |
| - it != resources.end(); ++it) { |
| - Resource* resource = GetResource(*it); |
| - DCHECK(!resource->locked_for_write); |
| - DCHECK(!resource->lock_for_read_count); |
| - DCHECK(child_info.parent_to_child_map.find(*it) != |
| - child_info.parent_to_child_map.end()); |
| - |
| - if (resource->filter != resource->original_filter) { |
| - DCHECK(resource->target); |
| - DCHECK(resource->gl_id); |
| - |
| - GLC(context3d, context3d->bindTexture(resource->target, resource->gl_id)); |
| - GLC(context3d, context3d->texParameteri(resource->target, |
| - GL_TEXTURE_MIN_FILTER, |
| - resource->original_filter)); |
| - GLC(context3d, context3d->texParameteri(resource->target, |
| - GL_TEXTURE_MAG_FILTER, |
| - resource->original_filter)); |
| - } |
| - |
| - ReturnedResource returned; |
| - returned.id = child_info.parent_to_child_map[*it]; |
| - returned.sync_point = resource->mailbox.sync_point(); |
| - if (!returned.sync_point) |
| - need_sync_point = true; |
| - returned.count = resource->imported_count; |
| - list->push_back(returned); |
| - |
| - child_info.parent_to_child_map.erase(*it); |
| - child_info.child_to_parent_map.erase(returned.id); |
| - resources_[*it].imported_count = 0; |
| - DeleteResource(*it); |
| - } |
| - if (need_sync_point) { |
| - unsigned int sync_point = context3d->insertSyncPoint(); |
| - for (ReturnedResourceArray::iterator it = list->begin(); |
| - it != list->end(); ++it) { |
| - if (!it->sync_point) |
| - it->sync_point = sync_point; |
| - } |
| - } |
| -} |
| - |
| void ResourceProvider::ReceiveFromChild( |
| int child, const TransferableResourceArray& resources) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| @@ -931,20 +886,54 @@ void ResourceProvider::ReceiveFromChild( |
| GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, texture_id)); |
| GLC(context3d, context3d->consumeTextureCHROMIUM(GL_TEXTURE_2D, |
| it->mailbox.name)); |
| - ResourceId id = next_id_++; |
| - Resource resource( |
| - texture_id, it->size, it->format, it->filter, 0, GL_CLAMP_TO_EDGE, |
| - TextureUsageAny); |
| + ResourceId local_id = next_id_++; |
| + Resource resource(texture_id, |
| + it->size, |
| + it->format, |
| + it->filter, |
| + 0, |
| + GL_CLAMP_TO_EDGE, |
| + TextureUsageAny); |
| resource.mailbox.SetName(it->mailbox); |
| + resource.child_id = child; |
| // Don't allocate a texture for a child. |
| resource.allocated = true; |
| resource.imported_count = 1; |
| - resources_[id] = resource; |
| - child_info.parent_to_child_map[id] = it->id; |
| - child_info.child_to_parent_map[it->id] = id; |
| + resources_[local_id] = resource; |
| + child_info.parent_to_child_map[local_id] = it->id; |
| + child_info.child_to_parent_map[it->id] = local_id; |
| } |
| } |
| +void ResourceProvider::DeclareUsedResourcesFromChild( |
| + int child, |
| + const ResourceIdArray& resources_from_child) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + Child& child_info = children_.find(child)->second; |
| + child_info.in_use_resources.clear(); |
| + |
| + for (size_t i = 0; i < resources_from_child.size(); ++i) { |
| + ResourceIdMap::iterator it = |
| + child_info.child_to_parent_map.find(resources_from_child[i]); |
| + DCHECK(it != child_info.child_to_parent_map.end()); |
| + |
| + ResourceId local_id = it->second; |
| + child_info.in_use_resources.insert(local_id); |
| + } |
| + |
| + ResourceIdArray unused; |
| + for (ResourceIdMap::iterator 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 = child_info.in_use_resources.count(local_id); |
| + if (!resource_is_in_use) |
| + unused.push_back(local_id); |
| + } |
| + DeleteAndReturnUnusedResourcesToChild(&child_info, Normal, unused); |
| +} |
| + |
| void ResourceProvider::ReceiveReturnsFromParent( |
| const ReturnedResourceArray& resources) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| @@ -953,16 +942,29 @@ void ResourceProvider::ReceiveReturnsFromParent( |
| // TODO(skaslev): Implement this path for software compositing. |
| return; |
| } |
| + |
| + int child_id = 0; |
| + Child* child_info = NULL; |
| + ResourceIdArray resources_for_child; |
| + |
| for (ReturnedResourceArray::const_iterator it = resources.begin(); |
| it != resources.end(); |
| ++it) { |
| - ResourceMap::iterator map_iterator = resources_.find(it->id); |
| - DCHECK(map_iterator != resources_.end()); |
| + ResourceId local_id = it->id; |
| + ResourceMap::iterator map_iterator = resources_.find(local_id); |
| + |
| + // Resource was already lost (e.g. it belonged to a child that was |
| + // destroyed). |
| + if (map_iterator == resources_.end()) |
| + continue; |
| + |
| Resource* resource = &map_iterator->second; |
| + |
| CHECK_GE(resource->exported_count, it->count); |
| resource->exported_count -= it->count; |
| if (resource->exported_count) |
| continue; |
| + |
| if (resource->gl_id) { |
| if (it->sync_point) |
| GLC(context3d, context3d->waitSyncPoint(it->sync_point)); |
| @@ -970,8 +972,38 @@ void ResourceProvider::ReceiveReturnsFromParent( |
| resource->mailbox = |
| TextureMailbox(resource->mailbox.name(), it->sync_point); |
| } |
| - if (resource->marked_for_deletion) |
| - DeleteResourceInternal(map_iterator, Normal); |
| + |
| + if (!resource->marked_for_deletion) |
| + continue; |
| + if (!resource->child_id) |
| + continue; |
| + |
| + // Delete the resource and return it to a child if it came from one. |
| + if (resource->child_id != child_id) { |
| + ChildMap::iterator child_it = children_.find(resource->child_id); |
| + if (child_it == children_.end()) { |
| + // The child is gone and has abandoned the resource. |
|
piman
2013/09/20 01:55:01
How can this happen? If the child is gone, we remo
danakj
2013/09/20 14:54:52
Hm.. let me double check.. maybe this condition is
danakj
2013/09/20 17:35:12
DCHECKing here pointed out that Resource::child_id
|
| + DeleteResourceInternal(map_iterator, Normal); |
| + continue; |
| + } |
| + |
| + if (child_id) { |
| + DCHECK_NE(resources_for_child.size(), 0u); |
| + DeleteAndReturnUnusedResourcesToChild( |
| + child_info, Normal, resources_for_child); |
| + resources_for_child.clear(); |
|
piman
2013/09/20 01:55:01
Not sure how "coherent" the child ids will be in p
danakj
2013/09/20 14:54:52
I thought about a per-child id map but I thought i
danakj
2013/09/20 17:35:12
Implementing sorting required some fun with pairs,
|
| + } |
| + |
| + child_info = &child_it->second; |
| + child_id = resource->child_id; |
| + } |
| + resources_for_child.push_back(local_id); |
| + } |
| + |
| + if (child_id) { |
| + DCHECK_NE(resources_for_child.size(), 0u); |
| + DeleteAndReturnUnusedResourcesToChild( |
| + child_info, Normal, resources_for_child); |
| } |
| } |
| @@ -1009,6 +1041,93 @@ void ResourceProvider::TransferResource(WebGraphicsContext3D* context, |
| } |
| } |
| +void ResourceProvider::DeleteAndReturnUnusedResourcesToChild( |
| + Child* child_info, |
| + DeleteStyle style, |
| + const ResourceIdArray& unused) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(child_info); |
| + |
| + if (unused.empty()) |
| + return; |
| + |
| + WebGraphicsContext3D* context3d = Context3d(); |
| + if (!context3d || !context3d->makeContextCurrent()) { |
| + // TODO(skaslev): Implement this path for software compositing. |
| + return; |
| + } |
| + |
| + ReturnedResourceArray to_return; |
| + |
| + bool need_sync_point = false; |
| + for (size_t i = 0; i < unused.size(); ++i) { |
| + ResourceId local_id = unused[i]; |
| + |
| + ResourceMap::iterator it = resources_.find(local_id); |
| + CHECK(it != resources_.end()); |
| + Resource& resource = it->second; |
| + |
| + DCHECK(!resource.locked_for_write); |
| + DCHECK(!resource.lock_for_read_count); |
| + DCHECK_EQ(0u, child_info->in_use_resources.count(local_id)); |
| + DCHECK(child_info->parent_to_child_map.count(local_id)); |
| + |
| + ResourceId child_id = child_info->parent_to_child_map[local_id]; |
| + DCHECK(child_info->child_to_parent_map.count(child_id)); |
| + |
| + bool is_lost = false; |
| + if (resource.exported_count > 0) { |
| + if (style != ForShutdown) { |
| + // Defer this until we receive the resource back from the parent. |
| + resource.marked_for_deletion = true; |
| + continue; |
| + } |
| + |
| + // We still have an exported_count, so we'll have to lose it. |
| + is_lost = true; |
| + } |
| + |
| + if (resource.filter != resource.original_filter) { |
| + DCHECK(resource.target); |
| + DCHECK(resource.gl_id); |
| + |
| + GLC(context3d, context3d->bindTexture(resource.target, resource.gl_id)); |
| + GLC(context3d, |
| + context3d->texParameteri(resource.target, |
| + GL_TEXTURE_MIN_FILTER, |
| + resource.original_filter)); |
| + GLC(context3d, |
| + context3d->texParameteri(resource.target, |
| + GL_TEXTURE_MAG_FILTER, |
| + resource.original_filter)); |
| + } |
| + |
| + ReturnedResource returned; |
| + returned.id = child_id; |
| + returned.sync_point = resource.mailbox.sync_point(); |
| + if (!returned.sync_point) |
| + need_sync_point = true; |
| + returned.count = resource.imported_count; |
| + // TODO(danakj): Save the |is_lost| bit. |
| + to_return.push_back(returned); |
| + |
| + child_info->parent_to_child_map.erase(local_id); |
| + child_info->child_to_parent_map.erase(child_id); |
| + resource.imported_count = 0; |
| + DeleteResourceInternal(it, style); |
| + } |
| + if (need_sync_point) { |
| + unsigned int sync_point = context3d->insertSyncPoint(); |
| + for (size_t i = 0; i < to_return.size(); ++i) { |
| + if (!to_return[i].sync_point) |
| + to_return[i].sync_point = sync_point; |
| + } |
| + } |
| + |
| + if (!to_return.empty()) |
| + child_info->return_callback.Run(to_return); |
| +} |
| + |
| void ResourceProvider::AcquirePixelBuffer(ResourceId id) { |
| Resource* resource = GetResource(id); |
| DCHECK(!resource->external); |