Index: cc/resources/video_resource_updater.cc |
diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc |
index 6c4f189aaff301863d7dc7c250e86589fa7d269c..be382ce4084c433ee3f8fa4052ab6af9a243c203 100644 |
--- a/cc/resources/video_resource_updater.cc |
+++ b/cc/resources/video_resource_updater.cc |
@@ -173,6 +173,56 @@ VideoResourceUpdater::~VideoResourceUpdater() { |
} |
VideoResourceUpdater::ResourceList::iterator |
+VideoResourceUpdater::RecycleOrAllocateResource( |
+ const gfx::Size& resource_size, |
+ ResourceFormat resource_format, |
+ const gfx::ColorSpace& color_space, |
+ bool software_resource, |
+ bool immutable_hint, |
+ int unique_id, |
+ int plane_index) { |
+ ResourceList::iterator recyclable_resource = all_resources_.end(); |
+ for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) { |
+ // If the plane index is valid (positive, or 0, meaning all planes) |
+ // then we are allowed to return a referenced resource that already |
+ // contains the right frame data. It's safe to reuse it even if |
+ // resource_provider_ holds some references to it, because those |
+ // references are read-only. |
+ if (plane_index != -1 && it->Matches(unique_id, plane_index)) { |
+ DCHECK(it->resource_size() == resource_size); |
+ DCHECK(it->resource_format() == resource_format); |
+ DCHECK(it->mailbox().IsZero() == software_resource); |
+ return it; |
+ } |
+ |
+ // Otherwise check whether this is an unreferenced resource of the right |
+ // format that we can recycle. Remember it, but don't return immediately, |
+ // because we still want to find any reusable resources. |
+ // Resources backed by SharedMemory are not ref-counted, unlike mailboxes, |
+ // so the definition of |in_use| must take this into account. Full |
+ // discussion in codereview.chromium.org/145273021. |
+ const bool in_use = |
+ it->has_refs() || |
+ (software_resource && |
+ resource_provider_->InUseByConsumer(it->resource_id())); |
+ |
+ if (!in_use && it->resource_size() == resource_size && |
+ it->resource_format() == resource_format && |
+ it->mailbox().IsZero() == software_resource && |
+ resource_provider_->IsImmutable(it->resource_id()) == immutable_hint) { |
+ recyclable_resource = it; |
+ } |
+ } |
+ |
+ if (recyclable_resource != all_resources_.end()) |
+ return recyclable_resource; |
+ |
+ // There was nothing available to reuse or recycle. Allocate a new resource. |
+ return AllocateResource(resource_size, resource_format, color_space, |
+ !software_resource, immutable_hint); |
+} |
+ |
+VideoResourceUpdater::ResourceList::iterator |
VideoResourceUpdater::AllocateResource(const gfx::Size& plane_size, |
ResourceFormat format, |
const gfx::ColorSpace& color_space, |
@@ -184,8 +234,7 @@ VideoResourceUpdater::AllocateResource(const gfx::Size& plane_size, |
plane_size, immutable_hint ? ResourceProvider::TEXTURE_HINT_IMMUTABLE |
: ResourceProvider::TEXTURE_HINT_DEFAULT, |
format, color_space); |
- if (resource_id == 0) |
- return all_resources_.end(); |
+ DCHECK_NE(resource_id, 0u); |
gpu::Mailbox mailbox; |
if (has_mailbox) { |
@@ -298,15 +347,23 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( |
ResourceFormat output_resource_format = |
resource_provider_->YuvResourceFormat(bits_per_channel); |
+ // If GPU compositing is enabled, but the output resource format |
+ // returned by the resource provider is RGBA_8888, then a GPU driver |
+ // bug workaround requires that YUV frames must be converted to RGB |
+ // before texture upload. |
+ bool texture_needs_rgb_conversion = |
+ !software_compositor && |
+ output_resource_format == ResourceFormat::RGBA_8888; |
size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_format); |
// TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB |
// conversion here. That involves an extra copy of each frame to a bitmap. |
// Obviously, this is suboptimal and should be addressed once ubercompositor |
// starts shaping up. |
- if (software_compositor) { |
+ if (software_compositor || texture_needs_rgb_conversion) { |
output_resource_format = kRGBResourceFormat; |
output_plane_count = 1; |
+ bits_per_channel = 8; |
} |
// Drop recycled resources that are the wrong format. |
@@ -328,83 +385,73 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( |
break; |
} |
- // Try recycle a previously-allocated resource. |
- ResourceList::iterator resource_it = all_resources_.end(); |
- for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) { |
- if (it->resource_size() == output_plane_resource_size && |
- it->resource_format() == output_resource_format) { |
- if (it->Matches(video_frame->unique_id(), i)) { |
- // Bingo, we found a resource that already contains the data we are |
- // planning to put in it. It's safe to reuse it even if |
- // resource_provider_ holds some references to it, because those |
- // references are read-only. |
- resource_it = it; |
- break; |
- } |
- |
- // This extra check is needed because resources backed by SharedMemory |
- // are not ref-counted, unlike mailboxes. Full discussion in |
- // codereview.chromium.org/145273021. |
- const bool in_use = |
- software_compositor && |
- resource_provider_->InUseByConsumer(it->resource_id()); |
- if (!it->has_refs() && !in_use) { |
- // We found a resource with the correct size that we can overwrite. |
- resource_it = it; |
- } |
- } |
- } |
- |
- // Check if we need to allocate a new resource. |
- if (resource_it == all_resources_.end()) { |
- const bool is_immutable = true; |
- resource_it = AllocateResource( |
- output_plane_resource_size, output_resource_format, |
- video_frame->ColorSpace(), !software_compositor, is_immutable); |
- } |
- if (resource_it == all_resources_.end()) |
- break; |
+ const bool is_immutable = true; |
+ ResourceList::iterator resource_it = RecycleOrAllocateResource( |
+ output_plane_resource_size, output_resource_format, |
+ video_frame->ColorSpace(), software_compositor, is_immutable, |
+ video_frame->unique_id(), i); |
resource_it->add_ref(); |
plane_resources.push_back(resource_it); |
} |
- if (plane_resources.size() != output_plane_count) { |
- // Allocation failed, nothing will be returned so restore reference counts. |
- for (ResourceList::iterator resource_it : plane_resources) |
- resource_it->remove_ref(); |
- return VideoFrameExternalResources(); |
- } |
- |
VideoFrameExternalResources external_resources; |
external_resources.bits_per_channel = bits_per_channel; |
- if (software_compositor) { |
+ if (software_compositor || texture_needs_rgb_conversion) { |
DCHECK_EQ(plane_resources.size(), 1u); |
PlaneResource& plane_resource = *plane_resources[0]; |
DCHECK_EQ(plane_resource.resource_format(), kRGBResourceFormat); |
- DCHECK(plane_resource.mailbox().IsZero()); |
+ DCHECK_EQ(software_compositor, plane_resource.mailbox().IsZero()); |
if (!plane_resource.Matches(video_frame->unique_id(), 0)) { |
// We need to transfer data from |video_frame| to the plane resource. |
- if (!video_renderer_) |
- video_renderer_.reset(new media::SkCanvasVideoRenderer); |
- |
- ResourceProvider::ScopedWriteLockSoftware lock( |
- resource_provider_, plane_resource.resource_id()); |
- SkCanvas canvas(lock.sk_bitmap()); |
- // This is software path, so canvas and video_frame are always backed |
- // by software. |
- video_renderer_->Copy(video_frame, &canvas, media::Context3D()); |
+ if (software_compositor) { |
+ if (!video_renderer_) |
+ video_renderer_.reset(new media::SkCanvasVideoRenderer); |
+ |
+ ResourceProvider::ScopedWriteLockSoftware lock( |
+ resource_provider_, plane_resource.resource_id()); |
+ SkCanvas canvas(lock.sk_bitmap()); |
+ // This is software path, so canvas and video_frame are always backed |
+ // by software. |
+ video_renderer_->Copy(video_frame, &canvas, media::Context3D()); |
+ } else { |
+ size_t bytes_per_row = ResourceUtil::CheckedWidthInBytes<size_t>( |
+ video_frame->coded_size().width(), ResourceFormat::RGBA_8888); |
+ size_t needed_size = bytes_per_row * video_frame->coded_size().height(); |
+ if (upload_pixels_.size() < needed_size) |
+ upload_pixels_.resize(needed_size); |
+ |
+ media::SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels( |
+ video_frame.get(), &upload_pixels_[0], bytes_per_row); |
+ |
+ resource_provider_->CopyToResource(plane_resource.resource_id(), |
+ &upload_pixels_[0], |
+ plane_resource.resource_size()); |
+ } |
plane_resource.SetUniqueId(video_frame->unique_id(), 0); |
} |
- external_resources.software_resources.push_back( |
- plane_resource.resource_id()); |
- external_resources.software_release_callback = |
- base::Bind(&RecycleResource, AsWeakPtr(), plane_resource.resource_id()); |
- external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; |
+ if (software_compositor) { |
+ external_resources.software_resources.push_back( |
+ plane_resource.resource_id()); |
+ external_resources.software_release_callback = base::Bind( |
+ &RecycleResource, AsWeakPtr(), plane_resource.resource_id()); |
+ external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; |
+ } else { |
+ // VideoResourceUpdater shares a context with the compositor so |
+ // a sync token is not required. |
+ TextureMailbox mailbox(plane_resource.mailbox(), gpu::SyncToken(), |
+ resource_provider_->GetResourceTextureTarget( |
+ plane_resource.resource_id())); |
+ mailbox.set_color_space(video_frame->ColorSpace()); |
+ external_resources.mailboxes.push_back(mailbox); |
+ external_resources.release_callbacks.push_back(base::Bind( |
+ &RecycleResource, AsWeakPtr(), plane_resource.resource_id())); |
+ external_resources.type = VideoFrameExternalResources::RGBA_RESOURCE; |
+ } |
return external_resources; |
} |
@@ -425,12 +472,12 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( |
// uploading (including non-frame data to fill in the stride). |
int video_stride_bytes = video_frame->stride(i); |
- size_t bytes_per_row = ResourceUtil::UncheckedWidthInBytes<size_t>( |
+ size_t bytes_per_row = ResourceUtil::CheckedWidthInBytes<size_t>( |
resource_size_pixels.width(), plane_resource.resource_format()); |
// Use 4-byte row alignment (OpenGL default) for upload performance. |
// Assuming that GL_UNPACK_ALIGNMENT has not changed from default. |
size_t upload_image_stride = |
- MathUtil::UncheckedRoundUp<size_t>(bytes_per_row, 4u); |
+ MathUtil::CheckedRoundUp<size_t>(bytes_per_row, 4u); |
bool needs_conversion = false; |
int shift = 0; |
@@ -519,6 +566,8 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( |
external_resources.multiplier = 2048.0 / max_input_value; |
} |
+ // VideoResourceUpdater shares a context with the compositor so a |
+ // sync token is not required. |
TextureMailbox mailbox(plane_resource.mailbox(), gpu::SyncToken(), |
resource_provider_->GetResourceTextureTarget( |
plane_resource.resource_id())); |
@@ -565,29 +614,13 @@ void VideoResourceUpdater::CopyPlaneTexture( |
// target to avoid loss of precision or dropping any alpha component. |
const ResourceFormat copy_target_format = ResourceFormat::RGBA_8888; |
- // Search for an existing resource to reuse. |
- VideoResourceUpdater::ResourceList::iterator resource = all_resources_.end(); |
- |
- for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) { |
- // Reuse resource if attributes match and the resource is a currently |
- // unreferenced texture. |
- if (it->resource_size() == output_plane_resource_size && |
- it->resource_format() == copy_target_format && |
- !it->mailbox().IsZero() && !it->has_refs() && |
- resource_provider_->GetTextureHint(it->resource_id()) != |
- ResourceProvider::TEXTURE_HINT_IMMUTABLE) { |
- resource = it; |
- break; |
- } |
- } |
- |
- // Otherwise allocate a new resource. |
- if (resource == all_resources_.end()) { |
- const bool is_immutable = false; |
- resource = AllocateResource(output_plane_resource_size, copy_target_format, |
- video_frame->ColorSpace(), true, is_immutable); |
- } |
- |
+ const bool is_immutable = false; |
+ const int no_unique_id = 0; |
+ const int no_plane_index = -1; // Do not recycle referenced textures. |
+ VideoResourceUpdater::ResourceList::iterator resource = |
+ RecycleOrAllocateResource(output_plane_resource_size, copy_target_format, |
+ video_frame->ColorSpace(), false, is_immutable, |
+ no_unique_id, no_plane_index); |
resource->add_ref(); |
ResourceProvider::ScopedWriteLockGL lock(resource_provider_, |
@@ -679,7 +712,6 @@ void VideoResourceUpdater::RecycleResource( |
// Resource was already deleted. |
return; |
} |
- |
const ResourceList::iterator resource_it = std::find_if( |
updater->all_resources_.begin(), updater->all_resources_.end(), |
[resource_id](const PlaneResource& plane_resource) { |