| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "cc/resources/video_resource_updater.h" | 5 #include "cc/resources/video_resource_updater.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| 11 | 11 |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/bit_cast.h" | 13 #include "base/bit_cast.h" |
| 14 #include "base/trace_event/trace_event.h" | 14 #include "base/trace_event/trace_event.h" |
| 15 #include "cc/base/math_util.h" | 15 #include "cc/base/math_util.h" |
| 16 #include "cc/output/gl_renderer.h" | 16 #include "cc/output/gl_renderer.h" |
| 17 #include "cc/resources/resource_provider.h" | 17 #include "cc/resources/resource_provider.h" |
| 18 #include "gpu/GLES2/gl2extchromium.h" | 18 #include "gpu/GLES2/gl2extchromium.h" |
| 19 #include "gpu/command_buffer/client/gles2_interface.h" | 19 #include "gpu/command_buffer/client/gles2_interface.h" |
| 20 #include "media/base/video_frame.h" | 20 #include "media/base/video_frame.h" |
| 21 #include "media/renderers/skcanvas_video_renderer.h" | 21 #include "media/renderers/skcanvas_video_renderer.h" |
| 22 #include "third_party/khronos/GLES2/gl2.h" | 22 #include "third_party/khronos/GLES2/gl2.h" |
| 23 #include "third_party/khronos/GLES2/gl2ext.h" | 23 #include "third_party/khronos/GLES2/gl2ext.h" |
| 24 #include "third_party/skia/include/core/SkCanvas.h" | 24 #include "third_party/skia/include/core/SkCanvas.h" |
| 25 #include "ui/gfx/geometry/size_conversions.h" | 25 #include "ui/gfx/geometry/size_conversions.h" |
| 26 | 26 |
| 27 #if DCHECK_IS_ON() | |
| 28 #include "base/single_thread_task_runner.h" | |
| 29 #include "base/threading/thread_task_runner_handle.h" | |
| 30 #endif | |
| 31 | |
| 32 namespace cc { | 27 namespace cc { |
| 33 | 28 |
| 34 namespace { | 29 namespace { |
| 35 | 30 |
| 36 const ResourceFormat kRGBResourceFormat = RGBA_8888; | 31 const ResourceFormat kRGBResourceFormat = RGBA_8888; |
| 37 | 32 |
| 38 VideoFrameExternalResources::ResourceType ResourceTypeForVideoFrame( | 33 VideoFrameExternalResources::ResourceType ResourceTypeForVideoFrame( |
| 39 media::VideoFrame* video_frame) { | 34 media::VideoFrame* video_frame) { |
| 40 switch (video_frame->format()) { | 35 switch (video_frame->format()) { |
| 41 case media::PIXEL_FORMAT_ARGB: | 36 case media::PIXEL_FORMAT_ARGB: |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 117 sync_token_.Clear(); | 112 sync_token_.Clear(); |
| 118 } | 113 } |
| 119 } | 114 } |
| 120 } | 115 } |
| 121 | 116 |
| 122 private: | 117 private: |
| 123 gpu::gles2::GLES2Interface* gl_; | 118 gpu::gles2::GLES2Interface* gl_; |
| 124 gpu::SyncToken sync_token_; | 119 gpu::SyncToken sync_token_; |
| 125 }; | 120 }; |
| 126 | 121 |
| 127 #if DCHECK_IS_ON() | |
| 128 void OnVideoFrameDestruct( | |
| 129 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | |
| 130 const base::Closure& task) { | |
| 131 task_runner->PostTask(FROM_HERE, task); | |
| 132 } | |
| 133 #endif | |
| 134 | |
| 135 } // namespace | 122 } // namespace |
| 136 | 123 |
| 137 VideoResourceUpdater::PlaneResource::PlaneResource( | 124 VideoResourceUpdater::PlaneResource::PlaneResource( |
| 138 unsigned int resource_id, | 125 unsigned int resource_id, |
| 139 const gfx::Size& resource_size, | 126 const gfx::Size& resource_size, |
| 140 ResourceFormat resource_format, | 127 ResourceFormat resource_format, |
| 141 gpu::Mailbox mailbox) | 128 gpu::Mailbox mailbox) |
| 142 : resource_id(resource_id), | 129 : resource_id_(resource_id), |
| 143 resource_size(resource_size), | 130 resource_size_(resource_size), |
| 144 resource_format(resource_format), | 131 resource_format_(resource_format), |
| 145 mailbox(mailbox), | 132 mailbox_(mailbox) {} |
| 146 ref_count(0), | |
| 147 frame_ptr(nullptr), | |
| 148 #if DCHECK_IS_ON() | |
| 149 destructed(false), | |
| 150 #endif | |
| 151 plane_index(0u) { | |
| 152 } | |
| 153 | 133 |
| 154 VideoResourceUpdater::PlaneResource::PlaneResource(const PlaneResource& other) = | 134 VideoResourceUpdater::PlaneResource::PlaneResource(const PlaneResource& other) = |
| 155 default; | 135 default; |
| 156 | 136 |
| 157 bool VideoResourceUpdater::PlaneResourceMatchesUniqueID( | 137 bool VideoResourceUpdater::PlaneResource::Matches(int unique_frame_id, |
| 158 const PlaneResource& plane_resource, | 138 size_t plane_index) { |
| 159 const media::VideoFrame* video_frame, | 139 return has_unique_frame_id_and_plane_index_ && |
| 160 size_t plane_index) { | 140 unique_frame_id_ == unique_frame_id && plane_index_ == plane_index; |
| 161 bool matched = plane_resource.frame_ptr == video_frame && | |
| 162 plane_resource.plane_index == plane_index && | |
| 163 plane_resource.timestamp == video_frame->timestamp(); | |
| 164 #if DCHECK_IS_ON() | |
| 165 if ((plane_index == 0) && matched) { | |
| 166 DCHECK(!plane_resource.destructed) | |
| 167 << "ERROR: reused the destructed resource." | |
| 168 << " timestamp = " << plane_resource.timestamp; | |
| 169 } | |
| 170 #endif | |
| 171 return matched; | |
| 172 } | 141 } |
| 173 | 142 |
| 174 void VideoResourceUpdater::SetPlaneResourceUniqueId( | 143 void VideoResourceUpdater::PlaneResource::SetUniqueId(int unique_frame_id, |
| 175 const media::VideoFrame* video_frame, | 144 size_t plane_index) { |
| 176 size_t plane_index, | 145 DCHECK_EQ(ref_count_, 1); |
| 177 PlaneResource* plane_resource) { | 146 plane_index_ = plane_index; |
| 178 plane_resource->frame_ptr = video_frame; | 147 unique_frame_id_ = unique_frame_id; |
| 179 plane_resource->plane_index = plane_index; | 148 has_unique_frame_id_and_plane_index_ = true; |
| 180 plane_resource->timestamp = video_frame->timestamp(); | |
| 181 #if DCHECK_IS_ON() | |
| 182 plane_resource->destructed = false; | |
| 183 #endif | |
| 184 } | 149 } |
| 185 | 150 |
| 186 VideoFrameExternalResources::VideoFrameExternalResources() | 151 VideoFrameExternalResources::VideoFrameExternalResources() |
| 187 : type(NONE), | 152 : type(NONE), |
| 188 read_lock_fences_enabled(false), | 153 read_lock_fences_enabled(false), |
| 189 offset(0.0f), | 154 offset(0.0f), |
| 190 multiplier(1.0f) {} | 155 multiplier(1.0f) {} |
| 191 | 156 |
| 192 VideoFrameExternalResources::VideoFrameExternalResources( | 157 VideoFrameExternalResources::VideoFrameExternalResources( |
| 193 const VideoFrameExternalResources& other) = default; | 158 const VideoFrameExternalResources& other) = default; |
| 194 | 159 |
| 195 VideoFrameExternalResources::~VideoFrameExternalResources() {} | 160 VideoFrameExternalResources::~VideoFrameExternalResources() {} |
| 196 | 161 |
| 197 VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider, | 162 VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider, |
| 198 ResourceProvider* resource_provider) | 163 ResourceProvider* resource_provider) |
| 199 : context_provider_(context_provider), | 164 : context_provider_(context_provider), |
| 200 resource_provider_(resource_provider) { | 165 resource_provider_(resource_provider) { |
| 201 } | 166 } |
| 202 | 167 |
| 203 VideoResourceUpdater::~VideoResourceUpdater() { | 168 VideoResourceUpdater::~VideoResourceUpdater() { |
| 204 for (const PlaneResource& plane_resource : all_resources_) | 169 for (const PlaneResource& plane_resource : all_resources_) |
| 205 resource_provider_->DeleteResource(plane_resource.resource_id); | 170 resource_provider_->DeleteResource(plane_resource.resource_id()); |
| 206 } | 171 } |
| 207 | 172 |
| 208 VideoResourceUpdater::ResourceList::iterator | 173 VideoResourceUpdater::ResourceList::iterator |
| 209 VideoResourceUpdater::AllocateResource(const gfx::Size& plane_size, | 174 VideoResourceUpdater::AllocateResource(const gfx::Size& plane_size, |
| 210 ResourceFormat format, | 175 ResourceFormat format, |
| 211 bool has_mailbox, | 176 bool has_mailbox, |
| 212 bool immutable_hint) { | 177 bool immutable_hint) { |
| 213 // TODO(danakj): Abstract out hw/sw resource create/delete from | 178 // TODO(danakj): Abstract out hw/sw resource create/delete from |
| 214 // ResourceProvider and stop using ResourceProvider in this class. | 179 // ResourceProvider and stop using ResourceProvider in this class. |
| 215 const ResourceId resource_id = resource_provider_->CreateResource( | 180 const ResourceId resource_id = resource_provider_->CreateResource( |
| (...skipping 15 matching lines...) Expand all Loading... |
| 231 lock.texture_id(), | 196 lock.texture_id(), |
| 232 resource_provider_->GetResourceTextureTarget(resource_id), | 197 resource_provider_->GetResourceTextureTarget(resource_id), |
| 233 mailbox.name); | 198 mailbox.name); |
| 234 } | 199 } |
| 235 all_resources_.push_front( | 200 all_resources_.push_front( |
| 236 PlaneResource(resource_id, plane_size, format, mailbox)); | 201 PlaneResource(resource_id, plane_size, format, mailbox)); |
| 237 return all_resources_.begin(); | 202 return all_resources_.begin(); |
| 238 } | 203 } |
| 239 | 204 |
| 240 void VideoResourceUpdater::DeleteResource(ResourceList::iterator resource_it) { | 205 void VideoResourceUpdater::DeleteResource(ResourceList::iterator resource_it) { |
| 241 DCHECK_EQ(resource_it->ref_count, 0); | 206 DCHECK(!resource_it->has_refs()); |
| 242 resource_provider_->DeleteResource(resource_it->resource_id); | 207 resource_provider_->DeleteResource(resource_it->resource_id()); |
| 243 all_resources_.erase(resource_it); | 208 all_resources_.erase(resource_it); |
| 244 } | 209 } |
| 245 | 210 |
| 246 VideoFrameExternalResources | 211 VideoFrameExternalResources |
| 247 VideoResourceUpdater::CreateExternalResourcesFromVideoFrame( | 212 VideoResourceUpdater::CreateExternalResourcesFromVideoFrame( |
| 248 scoped_refptr<media::VideoFrame> video_frame) { | 213 scoped_refptr<media::VideoFrame> video_frame) { |
| 249 #if defined(VIDEO_HOLE) | 214 #if defined(VIDEO_HOLE) |
| 250 if (video_frame->storage_type() == media::VideoFrame::STORAGE_HOLE) { | 215 if (video_frame->storage_type() == media::VideoFrame::STORAGE_HOLE) { |
| 251 VideoFrameExternalResources external_resources; | 216 VideoFrameExternalResources external_resources; |
| 252 external_resources.type = VideoFrameExternalResources::HOLE; | 217 external_resources.type = VideoFrameExternalResources::HOLE; |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 335 // conversion here. That involves an extra copy of each frame to a bitmap. | 300 // conversion here. That involves an extra copy of each frame to a bitmap. |
| 336 // Obviously, this is suboptimal and should be addressed once ubercompositor | 301 // Obviously, this is suboptimal and should be addressed once ubercompositor |
| 337 // starts shaping up. | 302 // starts shaping up. |
| 338 if (software_compositor) { | 303 if (software_compositor) { |
| 339 output_resource_format = kRGBResourceFormat; | 304 output_resource_format = kRGBResourceFormat; |
| 340 output_plane_count = 1; | 305 output_plane_count = 1; |
| 341 } | 306 } |
| 342 | 307 |
| 343 // Drop recycled resources that are the wrong format. | 308 // Drop recycled resources that are the wrong format. |
| 344 for (auto it = all_resources_.begin(); it != all_resources_.end();) { | 309 for (auto it = all_resources_.begin(); it != all_resources_.end();) { |
| 345 if (it->ref_count == 0 && it->resource_format != output_resource_format) | 310 if (!it->has_refs() && it->resource_format() != output_resource_format) |
| 346 DeleteResource(it++); | 311 DeleteResource(it++); |
| 347 else | 312 else |
| 348 ++it; | 313 ++it; |
| 349 } | 314 } |
| 350 | 315 |
| 351 const int max_resource_size = resource_provider_->max_texture_size(); | 316 const int max_resource_size = resource_provider_->max_texture_size(); |
| 352 std::vector<ResourceList::iterator> plane_resources; | 317 std::vector<ResourceList::iterator> plane_resources; |
| 353 for (size_t i = 0; i < output_plane_count; ++i) { | 318 for (size_t i = 0; i < output_plane_count; ++i) { |
| 354 gfx::Size output_plane_resource_size = | 319 gfx::Size output_plane_resource_size = |
| 355 SoftwarePlaneDimension(video_frame.get(), software_compositor, i); | 320 SoftwarePlaneDimension(video_frame.get(), software_compositor, i); |
| 356 if (output_plane_resource_size.IsEmpty() || | 321 if (output_plane_resource_size.IsEmpty() || |
| 357 output_plane_resource_size.width() > max_resource_size || | 322 output_plane_resource_size.width() > max_resource_size || |
| 358 output_plane_resource_size.height() > max_resource_size) { | 323 output_plane_resource_size.height() > max_resource_size) { |
| 359 break; | 324 break; |
| 360 } | 325 } |
| 361 | 326 |
| 362 // Try recycle a previously-allocated resource. | 327 // Try recycle a previously-allocated resource. |
| 363 ResourceList::iterator resource_it = all_resources_.end(); | 328 ResourceList::iterator resource_it = all_resources_.end(); |
| 364 for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) { | 329 for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) { |
| 365 if (it->resource_size == output_plane_resource_size && | 330 if (it->resource_size() == output_plane_resource_size && |
| 366 it->resource_format == output_resource_format) { | 331 it->resource_format() == output_resource_format) { |
| 367 if (PlaneResourceMatchesUniqueID(*it, video_frame.get(), i)) { | 332 if (it->Matches(video_frame->unique_id(), i)) { |
| 368 // Bingo, we found a resource that already contains the data we are | 333 // Bingo, we found a resource that already contains the data we are |
| 369 // planning to put in it. It's safe to reuse it even if | 334 // planning to put in it. It's safe to reuse it even if |
| 370 // resource_provider_ holds some references to it, because those | 335 // resource_provider_ holds some references to it, because those |
| 371 // references are read-only. | 336 // references are read-only. |
| 372 resource_it = it; | 337 resource_it = it; |
| 373 break; | 338 break; |
| 374 } | 339 } |
| 375 | 340 |
| 376 // This extra check is needed because resources backed by SharedMemory | 341 // This extra check is needed because resources backed by SharedMemory |
| 377 // are not ref-counted, unlike mailboxes. Full discussion in | 342 // are not ref-counted, unlike mailboxes. Full discussion in |
| 378 // codereview.chromium.org/145273021. | 343 // codereview.chromium.org/145273021. |
| 379 const bool in_use = | 344 const bool in_use = |
| 380 software_compositor && | 345 software_compositor && |
| 381 resource_provider_->InUseByConsumer(it->resource_id); | 346 resource_provider_->InUseByConsumer(it->resource_id()); |
| 382 if (it->ref_count == 0 && !in_use) { | 347 if (!it->has_refs() && !in_use) { |
| 383 // We found a resource with the correct size that we can overwrite. | 348 // We found a resource with the correct size that we can overwrite. |
| 384 resource_it = it; | 349 resource_it = it; |
| 385 } | 350 } |
| 386 } | 351 } |
| 387 } | 352 } |
| 388 | 353 |
| 389 // Check if we need to allocate a new resource. | 354 // Check if we need to allocate a new resource. |
| 390 if (resource_it == all_resources_.end()) { | 355 if (resource_it == all_resources_.end()) { |
| 391 const bool is_immutable = true; | 356 const bool is_immutable = true; |
| 392 resource_it = | 357 resource_it = |
| 393 AllocateResource(output_plane_resource_size, output_resource_format, | 358 AllocateResource(output_plane_resource_size, output_resource_format, |
| 394 !software_compositor, is_immutable); | 359 !software_compositor, is_immutable); |
| 395 } | 360 } |
| 396 if (resource_it == all_resources_.end()) | 361 if (resource_it == all_resources_.end()) |
| 397 break; | 362 break; |
| 398 | 363 |
| 399 ++resource_it->ref_count; | 364 resource_it->add_ref(); |
| 400 plane_resources.push_back(resource_it); | 365 plane_resources.push_back(resource_it); |
| 401 } | 366 } |
| 402 | 367 |
| 403 if (plane_resources.size() != output_plane_count) { | 368 if (plane_resources.size() != output_plane_count) { |
| 404 // Allocation failed, nothing will be returned so restore reference counts. | 369 // Allocation failed, nothing will be returned so restore reference counts. |
| 405 for (ResourceList::iterator resource_it : plane_resources) | 370 for (ResourceList::iterator resource_it : plane_resources) |
| 406 --resource_it->ref_count; | 371 resource_it->remove_ref(); |
| 407 return VideoFrameExternalResources(); | 372 return VideoFrameExternalResources(); |
| 408 } | 373 } |
| 409 | 374 |
| 410 VideoFrameExternalResources external_resources; | 375 VideoFrameExternalResources external_resources; |
| 411 | 376 |
| 412 if (software_compositor) { | 377 if (software_compositor) { |
| 413 DCHECK_EQ(plane_resources.size(), 1u); | 378 DCHECK_EQ(plane_resources.size(), 1u); |
| 414 PlaneResource& plane_resource = *plane_resources[0]; | 379 PlaneResource& plane_resource = *plane_resources[0]; |
| 415 DCHECK_EQ(plane_resource.resource_format, kRGBResourceFormat); | 380 DCHECK_EQ(plane_resource.resource_format(), kRGBResourceFormat); |
| 416 DCHECK(plane_resource.mailbox.IsZero()); | 381 DCHECK(plane_resource.mailbox().IsZero()); |
| 417 | 382 |
| 418 if (!PlaneResourceMatchesUniqueID(plane_resource, video_frame.get(), 0)) { | 383 if (!plane_resource.Matches(video_frame->unique_id(), 0)) { |
| 419 // We need to transfer data from |video_frame| to the plane resource. | 384 // We need to transfer data from |video_frame| to the plane resource. |
| 420 if (!video_renderer_) | 385 if (!video_renderer_) |
| 421 video_renderer_.reset(new media::SkCanvasVideoRenderer); | 386 video_renderer_.reset(new media::SkCanvasVideoRenderer); |
| 422 | 387 |
| 423 ResourceProvider::ScopedWriteLockSoftware lock( | 388 ResourceProvider::ScopedWriteLockSoftware lock( |
| 424 resource_provider_, plane_resource.resource_id); | 389 resource_provider_, plane_resource.resource_id()); |
| 425 SkCanvas canvas(lock.sk_bitmap()); | 390 SkCanvas canvas(lock.sk_bitmap()); |
| 426 // This is software path, so canvas and video_frame are always backed | 391 // This is software path, so canvas and video_frame are always backed |
| 427 // by software. | 392 // by software. |
| 428 video_renderer_->Copy(video_frame, &canvas, media::Context3D()); | 393 video_renderer_->Copy(video_frame, &canvas, media::Context3D()); |
| 429 SetPlaneResourceUniqueId(video_frame.get(), 0, &plane_resource); | 394 plane_resource.SetUniqueId(video_frame->unique_id(), 0); |
| 430 #if DCHECK_IS_ON() | |
| 431 // Add VideoFrame destructor callback. | |
| 432 video_frame->AddDestructionObserver(base::Bind( | |
| 433 &OnVideoFrameDestruct, base::ThreadTaskRunnerHandle::Get(), | |
| 434 base::Bind(&VideoResourceUpdater::MarkOldResource, AsWeakPtr(), | |
| 435 base::Unretained(video_frame.get()), | |
| 436 video_frame->timestamp()))); | |
| 437 #endif | |
| 438 } | 395 } |
| 439 | 396 |
| 440 external_resources.software_resources.push_back(plane_resource.resource_id); | 397 external_resources.software_resources.push_back( |
| 398 plane_resource.resource_id()); |
| 441 external_resources.software_release_callback = | 399 external_resources.software_release_callback = |
| 442 base::Bind(&RecycleResource, AsWeakPtr(), plane_resource.resource_id); | 400 base::Bind(&RecycleResource, AsWeakPtr(), plane_resource.resource_id()); |
| 443 external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; | 401 external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; |
| 444 return external_resources; | 402 return external_resources; |
| 445 } | 403 } |
| 446 | 404 |
| 447 for (size_t i = 0; i < plane_resources.size(); ++i) { | 405 for (size_t i = 0; i < plane_resources.size(); ++i) { |
| 448 PlaneResource& plane_resource = *plane_resources[i]; | 406 PlaneResource& plane_resource = *plane_resources[i]; |
| 449 // Update each plane's resource id with its content. | 407 // Update each plane's resource id with its content. |
| 450 DCHECK_EQ(plane_resource.resource_format, | 408 DCHECK_EQ(plane_resource.resource_format(), |
| 451 resource_provider_->YuvResourceFormat(bits_per_channel)); | 409 resource_provider_->YuvResourceFormat(bits_per_channel)); |
| 452 | 410 |
| 453 if (!PlaneResourceMatchesUniqueID(plane_resource, video_frame.get(), i)) { | 411 if (!plane_resource.Matches(video_frame->unique_id(), i)) { |
| 454 // We need to transfer data from |video_frame| to the plane resource. | 412 // We need to transfer data from |video_frame| to the plane resource. |
| 455 // TODO(reveman): Can use GpuMemoryBuffers here to improve performance. | 413 // TODO(reveman): Can use GpuMemoryBuffers here to improve performance. |
| 456 | 414 |
| 457 // The |resource_size_pixels| is the size of the resource we want to | 415 // The |resource_size_pixels| is the size of the resource we want to |
| 458 // upload to. | 416 // upload to. |
| 459 gfx::Size resource_size_pixels = plane_resource.resource_size; | 417 gfx::Size resource_size_pixels = plane_resource.resource_size(); |
| 460 // The |video_stride_bytes| is the width of the video frame we are | 418 // The |video_stride_bytes| is the width of the video frame we are |
| 461 // uploading (including non-frame data to fill in the stride). | 419 // uploading (including non-frame data to fill in the stride). |
| 462 int video_stride_bytes = video_frame->stride(i); | 420 int video_stride_bytes = video_frame->stride(i); |
| 463 | 421 |
| 464 size_t bytes_per_row = ResourceUtil::UncheckedWidthInBytes<size_t>( | 422 size_t bytes_per_row = ResourceUtil::UncheckedWidthInBytes<size_t>( |
| 465 resource_size_pixels.width(), plane_resource.resource_format); | 423 resource_size_pixels.width(), plane_resource.resource_format()); |
| 466 // Use 4-byte row alignment (OpenGL default) for upload performance. | 424 // Use 4-byte row alignment (OpenGL default) for upload performance. |
| 467 // Assuming that GL_UNPACK_ALIGNMENT has not changed from default. | 425 // Assuming that GL_UNPACK_ALIGNMENT has not changed from default. |
| 468 size_t upload_image_stride = | 426 size_t upload_image_stride = |
| 469 MathUtil::UncheckedRoundUp<size_t>(bytes_per_row, 4u); | 427 MathUtil::UncheckedRoundUp<size_t>(bytes_per_row, 4u); |
| 470 | 428 |
| 471 bool needs_conversion = false; | 429 bool needs_conversion = false; |
| 472 int shift = 0; | 430 int shift = 0; |
| 473 | 431 |
| 474 // LUMINANCE_F16 uses half-floats, so we always need a conversion step. | 432 // LUMINANCE_F16 uses half-floats, so we always need a conversion step. |
| 475 if (plane_resource.resource_format == LUMINANCE_F16) { | 433 if (plane_resource.resource_format() == LUMINANCE_F16) { |
| 476 needs_conversion = true; | 434 needs_conversion = true; |
| 477 // Note that the current method of converting integers to half-floats | 435 // Note that the current method of converting integers to half-floats |
| 478 // stops working if you have more than 10 bits of data. | 436 // stops working if you have more than 10 bits of data. |
| 479 DCHECK_LE(bits_per_channel, 10); | 437 DCHECK_LE(bits_per_channel, 10); |
| 480 } else if (bits_per_channel > 8) { | 438 } else if (bits_per_channel > 8) { |
| 481 // If bits_per_channel > 8 and we can't use LUMINANCE_F16, we need to | 439 // If bits_per_channel > 8 and we can't use LUMINANCE_F16, we need to |
| 482 // shift the data down and create an 8-bit texture. | 440 // shift the data down and create an 8-bit texture. |
| 483 needs_conversion = true; | 441 needs_conversion = true; |
| 484 shift = bits_per_channel - 8; | 442 shift = bits_per_channel - 8; |
| 485 } | 443 } |
| 486 const uint8_t* pixels; | 444 const uint8_t* pixels; |
| 487 if (static_cast<int>(upload_image_stride) == video_stride_bytes && | 445 if (static_cast<int>(upload_image_stride) == video_stride_bytes && |
| 488 !needs_conversion) { | 446 !needs_conversion) { |
| 489 pixels = video_frame->data(i); | 447 pixels = video_frame->data(i); |
| 490 } else { | 448 } else { |
| 491 // Avoid malloc for each frame/plane if possible. | 449 // Avoid malloc for each frame/plane if possible. |
| 492 size_t needed_size = | 450 size_t needed_size = |
| 493 upload_image_stride * resource_size_pixels.height(); | 451 upload_image_stride * resource_size_pixels.height(); |
| 494 if (upload_pixels_.size() < needed_size) | 452 if (upload_pixels_.size() < needed_size) |
| 495 upload_pixels_.resize(needed_size); | 453 upload_pixels_.resize(needed_size); |
| 496 | 454 |
| 497 for (int row = 0; row < resource_size_pixels.height(); ++row) { | 455 for (int row = 0; row < resource_size_pixels.height(); ++row) { |
| 498 if (plane_resource.resource_format == LUMINANCE_F16) { | 456 if (plane_resource.resource_format() == LUMINANCE_F16) { |
| 499 uint16_t* dst = reinterpret_cast<uint16_t*>( | 457 uint16_t* dst = reinterpret_cast<uint16_t*>( |
| 500 &upload_pixels_[upload_image_stride * row]); | 458 &upload_pixels_[upload_image_stride * row]); |
| 501 const uint16_t* src = reinterpret_cast<uint16_t*>( | 459 const uint16_t* src = reinterpret_cast<uint16_t*>( |
| 502 video_frame->data(i) + (video_stride_bytes * row)); | 460 video_frame->data(i) + (video_stride_bytes * row)); |
| 503 // Micro-benchmarking indicates that the compiler does | 461 // Micro-benchmarking indicates that the compiler does |
| 504 // a good enough job of optimizing this loop that trying | 462 // a good enough job of optimizing this loop that trying |
| 505 // to manually operate on one uint64 at a time is not | 463 // to manually operate on one uint64 at a time is not |
| 506 // actually helpful. | 464 // actually helpful. |
| 507 // Note to future optimizers: Benchmark your optimizations! | 465 // Note to future optimizers: Benchmark your optimizations! |
| 508 for (size_t i = 0; i < bytes_per_row / 2; i++) | 466 for (size_t i = 0; i < bytes_per_row / 2; i++) |
| (...skipping 11 matching lines...) Expand all Loading... |
| 520 // differ in stride, copy one row at a time. | 478 // differ in stride, copy one row at a time. |
| 521 uint8_t* dst = &upload_pixels_[upload_image_stride * row]; | 479 uint8_t* dst = &upload_pixels_[upload_image_stride * row]; |
| 522 const uint8_t* src = | 480 const uint8_t* src = |
| 523 video_frame->data(i) + (video_stride_bytes * row); | 481 video_frame->data(i) + (video_stride_bytes * row); |
| 524 memcpy(dst, src, bytes_per_row); | 482 memcpy(dst, src, bytes_per_row); |
| 525 } | 483 } |
| 526 } | 484 } |
| 527 pixels = &upload_pixels_[0]; | 485 pixels = &upload_pixels_[0]; |
| 528 } | 486 } |
| 529 | 487 |
| 530 resource_provider_->CopyToResource(plane_resource.resource_id, pixels, | 488 resource_provider_->CopyToResource(plane_resource.resource_id(), pixels, |
| 531 resource_size_pixels); | 489 resource_size_pixels); |
| 532 SetPlaneResourceUniqueId(video_frame.get(), i, &plane_resource); | 490 plane_resource.SetUniqueId(video_frame->unique_id(), i); |
| 533 #if DCHECK_IS_ON() | |
| 534 // Add VideoFrame destructor callback. | |
| 535 if (i == 0) { | |
| 536 video_frame->AddDestructionObserver(base::Bind( | |
| 537 &OnVideoFrameDestruct, base::ThreadTaskRunnerHandle::Get(), | |
| 538 base::Bind(&VideoResourceUpdater::MarkOldResource, AsWeakPtr(), | |
| 539 base::Unretained(video_frame.get()), | |
| 540 video_frame->timestamp()))); | |
| 541 } | |
| 542 #endif | |
| 543 } | 491 } |
| 544 | 492 |
| 545 if (plane_resource.resource_format == LUMINANCE_F16) { | 493 if (plane_resource.resource_format() == LUMINANCE_F16) { |
| 546 // By OR-ing with 0x3800, 10-bit numbers become half-floats in the | 494 // By OR-ing with 0x3800, 10-bit numbers become half-floats in the |
| 547 // range [0.5..1) and 9-bit numbers get the range [0.5..0.75). | 495 // range [0.5..1) and 9-bit numbers get the range [0.5..0.75). |
| 548 // | 496 // |
| 549 // Half-floats are evaluated as: | 497 // Half-floats are evaluated as: |
| 550 // float value = pow(2.0, exponent - 25) * (0x400 + fraction); | 498 // float value = pow(2.0, exponent - 25) * (0x400 + fraction); |
| 551 // | 499 // |
| 552 // In our case the exponent is 14 (since we or with 0x3800) and | 500 // In our case the exponent is 14 (since we or with 0x3800) and |
| 553 // pow(2.0, 14-25) * 0x400 evaluates to 0.5 (our offset) and | 501 // pow(2.0, 14-25) * 0x400 evaluates to 0.5 (our offset) and |
| 554 // pow(2.0, 14-25) * fraction is [0..0.49951171875] for 10-bit and | 502 // pow(2.0, 14-25) * fraction is [0..0.49951171875] for 10-bit and |
| 555 // [0..0.24951171875] for 9-bit. | 503 // [0..0.24951171875] for 9-bit. |
| 556 // | 504 // |
| 557 // (https://en.wikipedia.org/wiki/Half-precision_floating-point_format) | 505 // (https://en.wikipedia.org/wiki/Half-precision_floating-point_format) |
| 558 // | 506 // |
| 559 // PLEASE NOTE: This doesn't work if bits_per_channel is > 10. | 507 // PLEASE NOTE: This doesn't work if bits_per_channel is > 10. |
| 560 // PLEASE NOTE: All planes are assumed to use the same multiplier/offset. | 508 // PLEASE NOTE: All planes are assumed to use the same multiplier/offset. |
| 561 external_resources.offset = 0.5f; | 509 external_resources.offset = 0.5f; |
| 562 // Max value from input data. | 510 // Max value from input data. |
| 563 int max_input_value = (1 << bits_per_channel) - 1; | 511 int max_input_value = (1 << bits_per_channel) - 1; |
| 564 // 2 << 11 = 2048 would be 1.0 with our exponent. | 512 // 2 << 11 = 2048 would be 1.0 with our exponent. |
| 565 external_resources.multiplier = 2048.0 / max_input_value; | 513 external_resources.multiplier = 2048.0 / max_input_value; |
| 566 } | 514 } |
| 567 | 515 |
| 568 external_resources.mailboxes.push_back( | 516 external_resources.mailboxes.push_back( |
| 569 TextureMailbox(plane_resource.mailbox, gpu::SyncToken(), | 517 TextureMailbox(plane_resource.mailbox(), gpu::SyncToken(), |
| 570 resource_provider_->GetResourceTextureTarget( | 518 resource_provider_->GetResourceTextureTarget( |
| 571 plane_resource.resource_id))); | 519 plane_resource.resource_id()))); |
| 572 external_resources.release_callbacks.push_back( | 520 external_resources.release_callbacks.push_back(base::Bind( |
| 573 base::Bind(&RecycleResource, AsWeakPtr(), plane_resource.resource_id)); | 521 &RecycleResource, AsWeakPtr(), plane_resource.resource_id())); |
| 574 } | 522 } |
| 575 | 523 |
| 576 external_resources.type = VideoFrameExternalResources::YUV_RESOURCE; | 524 external_resources.type = VideoFrameExternalResources::YUV_RESOURCE; |
| 577 return external_resources; | 525 return external_resources; |
| 578 } | 526 } |
| 579 | 527 |
| 580 // static | 528 // static |
| 581 void VideoResourceUpdater::ReturnTexture( | 529 void VideoResourceUpdater::ReturnTexture( |
| 582 base::WeakPtr<VideoResourceUpdater> updater, | 530 base::WeakPtr<VideoResourceUpdater> updater, |
| 583 const scoped_refptr<media::VideoFrame>& video_frame, | 531 const scoped_refptr<media::VideoFrame>& video_frame, |
| (...skipping 25 matching lines...) Expand all Loading... |
| 609 // The copy needs to be a direct transfer of pixel data, so we use an RGBA8 | 557 // The copy needs to be a direct transfer of pixel data, so we use an RGBA8 |
| 610 // target to avoid loss of precision or dropping any alpha component. | 558 // target to avoid loss of precision or dropping any alpha component. |
| 611 const ResourceFormat copy_target_format = ResourceFormat::RGBA_8888; | 559 const ResourceFormat copy_target_format = ResourceFormat::RGBA_8888; |
| 612 | 560 |
| 613 // Search for an existing resource to reuse. | 561 // Search for an existing resource to reuse. |
| 614 VideoResourceUpdater::ResourceList::iterator resource = all_resources_.end(); | 562 VideoResourceUpdater::ResourceList::iterator resource = all_resources_.end(); |
| 615 | 563 |
| 616 for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) { | 564 for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) { |
| 617 // Reuse resource if attributes match and the resource is a currently | 565 // Reuse resource if attributes match and the resource is a currently |
| 618 // unreferenced texture. | 566 // unreferenced texture. |
| 619 if (it->resource_size == output_plane_resource_size && | 567 if (it->resource_size() == output_plane_resource_size && |
| 620 it->resource_format == copy_target_format && !it->mailbox.IsZero() && | 568 it->resource_format() == copy_target_format && |
| 621 it->ref_count == 0 && | 569 !it->mailbox().IsZero() && !it->has_refs() && |
| 622 resource_provider_->GetTextureHint(it->resource_id) != | 570 resource_provider_->GetTextureHint(it->resource_id()) != |
| 623 ResourceProvider::TEXTURE_HINT_IMMUTABLE) { | 571 ResourceProvider::TEXTURE_HINT_IMMUTABLE) { |
| 624 resource = it; | 572 resource = it; |
| 625 break; | 573 break; |
| 626 } | 574 } |
| 627 } | 575 } |
| 628 | 576 |
| 629 // Otherwise allocate a new resource. | 577 // Otherwise allocate a new resource. |
| 630 if (resource == all_resources_.end()) { | 578 if (resource == all_resources_.end()) { |
| 631 const bool is_immutable = false; | 579 const bool is_immutable = false; |
| 632 resource = AllocateResource(output_plane_resource_size, copy_target_format, | 580 resource = AllocateResource(output_plane_resource_size, copy_target_format, |
| 633 true, is_immutable); | 581 true, is_immutable); |
| 634 } | 582 } |
| 635 | 583 |
| 636 ++resource->ref_count; | 584 resource->add_ref(); |
| 637 | 585 |
| 638 ResourceProvider::ScopedWriteLockGL lock(resource_provider_, | 586 ResourceProvider::ScopedWriteLockGL lock(resource_provider_, |
| 639 resource->resource_id); | 587 resource->resource_id()); |
| 640 uint32_t texture_id = lock.texture_id(); | 588 uint32_t texture_id = lock.texture_id(); |
| 641 | 589 |
| 642 DCHECK_EQ(resource_provider_->GetResourceTextureTarget(resource->resource_id), | 590 DCHECK_EQ( |
| 643 (GLenum)GL_TEXTURE_2D); | 591 resource_provider_->GetResourceTextureTarget(resource->resource_id()), |
| 592 (GLenum)GL_TEXTURE_2D); |
| 644 | 593 |
| 645 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); | 594 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); |
| 646 uint32_t src_texture_id = gl->CreateAndConsumeTextureCHROMIUM( | 595 uint32_t src_texture_id = gl->CreateAndConsumeTextureCHROMIUM( |
| 647 mailbox_holder.texture_target, mailbox_holder.mailbox.name); | 596 mailbox_holder.texture_target, mailbox_holder.mailbox.name); |
| 648 gl->CopySubTextureCHROMIUM(src_texture_id, texture_id, 0, 0, 0, 0, | 597 gl->CopySubTextureCHROMIUM(src_texture_id, texture_id, 0, 0, 0, 0, |
| 649 output_plane_resource_size.width(), | 598 output_plane_resource_size.width(), |
| 650 output_plane_resource_size.height(), false, false, | 599 output_plane_resource_size.height(), false, false, |
| 651 false); | 600 false); |
| 652 gl->DeleteTextures(1, &src_texture_id); | 601 gl->DeleteTextures(1, &src_texture_id); |
| 653 | 602 |
| 654 // Sync point for use of frame copy. | 603 // Sync point for use of frame copy. |
| 655 gpu::SyncToken sync_token; | 604 gpu::SyncToken sync_token; |
| 656 const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM(); | 605 const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM(); |
| 657 gl->ShallowFlushCHROMIUM(); | 606 gl->ShallowFlushCHROMIUM(); |
| 658 gl->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); | 607 gl->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); |
| 659 | 608 |
| 660 // Done with the source video frame texture at this point. | 609 // Done with the source video frame texture at this point. |
| 661 video_frame->UpdateReleaseSyncToken(&client); | 610 video_frame->UpdateReleaseSyncToken(&client); |
| 662 | 611 |
| 663 external_resources->mailboxes.push_back(TextureMailbox( | 612 external_resources->mailboxes.push_back(TextureMailbox( |
| 664 resource->mailbox, sync_token, GL_TEXTURE_2D, video_frame->coded_size(), | 613 resource->mailbox(), sync_token, GL_TEXTURE_2D, video_frame->coded_size(), |
| 665 gfx::GpuMemoryBufferId(), false, false)); | 614 gfx::GpuMemoryBufferId(), false, false)); |
| 666 | 615 |
| 667 external_resources->release_callbacks.push_back( | 616 external_resources->release_callbacks.push_back( |
| 668 base::Bind(&RecycleResource, AsWeakPtr(), resource->resource_id)); | 617 base::Bind(&RecycleResource, AsWeakPtr(), resource->resource_id())); |
| 669 } | 618 } |
| 670 | 619 |
| 671 VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( | 620 VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( |
| 672 scoped_refptr<media::VideoFrame> video_frame) { | 621 scoped_refptr<media::VideoFrame> video_frame) { |
| 673 TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForHardwarePlanes"); | 622 TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForHardwarePlanes"); |
| 674 DCHECK(video_frame->HasTextures()); | 623 DCHECK(video_frame->HasTextures()); |
| 675 if (!context_provider_) | 624 if (!context_provider_) |
| 676 return VideoFrameExternalResources(); | 625 return VideoFrameExternalResources(); |
| 677 | 626 |
| 678 VideoFrameExternalResources external_resources; | 627 VideoFrameExternalResources external_resources; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 721 bool lost_resource, | 670 bool lost_resource, |
| 722 BlockingTaskRunner* main_thread_task_runner) { | 671 BlockingTaskRunner* main_thread_task_runner) { |
| 723 if (!updater.get()) { | 672 if (!updater.get()) { |
| 724 // Resource was already deleted. | 673 // Resource was already deleted. |
| 725 return; | 674 return; |
| 726 } | 675 } |
| 727 | 676 |
| 728 const ResourceList::iterator resource_it = std::find_if( | 677 const ResourceList::iterator resource_it = std::find_if( |
| 729 updater->all_resources_.begin(), updater->all_resources_.end(), | 678 updater->all_resources_.begin(), updater->all_resources_.end(), |
| 730 [resource_id](const PlaneResource& plane_resource) { | 679 [resource_id](const PlaneResource& plane_resource) { |
| 731 return plane_resource.resource_id == resource_id; | 680 return plane_resource.resource_id() == resource_id; |
| 732 }); | 681 }); |
| 733 if (resource_it == updater->all_resources_.end()) | 682 if (resource_it == updater->all_resources_.end()) |
| 734 return; | 683 return; |
| 735 | 684 |
| 736 ContextProvider* context_provider = updater->context_provider_; | 685 ContextProvider* context_provider = updater->context_provider_; |
| 737 if (context_provider && sync_token.HasData()) { | 686 if (context_provider && sync_token.HasData()) { |
| 738 context_provider->ContextGL()->WaitSyncTokenCHROMIUM( | 687 context_provider->ContextGL()->WaitSyncTokenCHROMIUM( |
| 739 sync_token.GetConstData()); | 688 sync_token.GetConstData()); |
| 740 } | 689 } |
| 741 | 690 |
| 742 if (lost_resource) { | 691 if (lost_resource) { |
| 743 resource_it->ref_count = 0; | 692 resource_it->clear_refs(); |
| 744 updater->DeleteResource(resource_it); | 693 updater->DeleteResource(resource_it); |
| 745 return; | 694 return; |
| 746 } | 695 } |
| 747 | 696 |
| 748 --resource_it->ref_count; | 697 resource_it->remove_ref(); |
| 749 DCHECK_GE(resource_it->ref_count, 0); | |
| 750 } | 698 } |
| 751 | 699 |
| 752 #if DCHECK_IS_ON() | |
| 753 // static | |
| 754 void VideoResourceUpdater::MarkOldResource( | |
| 755 base::WeakPtr<VideoResourceUpdater> updater, | |
| 756 const media::VideoFrame* video_frame_ptr, | |
| 757 base::TimeDelta timestamp) { | |
| 758 if (!updater) | |
| 759 return; | |
| 760 const ResourceList::iterator resource_it = std::find_if( | |
| 761 updater->all_resources_.begin(), updater->all_resources_.end(), | |
| 762 [video_frame_ptr, timestamp](const PlaneResource& plane_resource) { | |
| 763 return plane_resource.frame_ptr == video_frame_ptr && | |
| 764 plane_resource.timestamp == timestamp && | |
| 765 plane_resource.plane_index == 0; | |
| 766 }); | |
| 767 if (resource_it == updater->all_resources_.end()) | |
| 768 return; | |
| 769 | |
| 770 resource_it->destructed = true; | |
| 771 } | |
| 772 #endif | |
| 773 | |
| 774 } // namespace cc | 700 } // namespace cc |
| OLD | NEW |