Chromium Code Reviews| 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> |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 165 : context_provider_(context_provider), | 165 : context_provider_(context_provider), |
| 166 resource_provider_(resource_provider) { | 166 resource_provider_(resource_provider) { |
| 167 } | 167 } |
| 168 | 168 |
| 169 VideoResourceUpdater::~VideoResourceUpdater() { | 169 VideoResourceUpdater::~VideoResourceUpdater() { |
| 170 for (const PlaneResource& plane_resource : all_resources_) | 170 for (const PlaneResource& plane_resource : all_resources_) |
| 171 resource_provider_->DeleteResource(plane_resource.resource_id()); | 171 resource_provider_->DeleteResource(plane_resource.resource_id()); |
| 172 } | 172 } |
| 173 | 173 |
| 174 VideoResourceUpdater::ResourceList::iterator | 174 VideoResourceUpdater::ResourceList::iterator |
| 175 VideoResourceUpdater::RecycleOrAllocateTexture( | |
| 176 const gfx::Size& resource_size, | |
| 177 ResourceFormat resource_format, | |
| 178 const gfx::ColorSpace& color_space, | |
| 179 bool immutable_hint) { | |
| 180 for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) { | |
| 181 // Reuse resource if attributes match and the resource is a currently | |
| 182 // unreferenced texture. | |
| 183 if (it->resource_size() == resource_size && | |
| 184 it->resource_format() == resource_format && !it->mailbox().IsZero() && | |
| 185 !it->has_refs() && | |
| 186 resource_provider_->GetTextureHint(it->resource_id()) != | |
| 187 ResourceProvider::TEXTURE_HINT_IMMUTABLE) { | |
| 188 return it; | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 // Otherwise allocate a new resource. | |
| 193 return AllocateResource(resource_size, resource_format, color_space, true, | |
| 194 immutable_hint); | |
| 195 } | |
| 196 | |
| 197 VideoResourceUpdater::ResourceList::iterator | |
| 175 VideoResourceUpdater::AllocateResource(const gfx::Size& plane_size, | 198 VideoResourceUpdater::AllocateResource(const gfx::Size& plane_size, |
| 176 ResourceFormat format, | 199 ResourceFormat format, |
| 177 const gfx::ColorSpace& color_space, | 200 const gfx::ColorSpace& color_space, |
| 178 bool has_mailbox, | 201 bool has_mailbox, |
| 179 bool immutable_hint) { | 202 bool immutable_hint) { |
| 180 // TODO(danakj): Abstract out hw/sw resource create/delete from | 203 // TODO(danakj): Abstract out hw/sw resource create/delete from |
| 181 // ResourceProvider and stop using ResourceProvider in this class. | 204 // ResourceProvider and stop using ResourceProvider in this class. |
| 182 const ResourceId resource_id = resource_provider_->CreateResource( | 205 const ResourceId resource_id = resource_provider_->CreateResource( |
| 183 plane_size, immutable_hint ? ResourceProvider::TEXTURE_HINT_IMMUTABLE | 206 plane_size, immutable_hint ? ResourceProvider::TEXTURE_HINT_IMMUTABLE |
| 184 : ResourceProvider::TEXTURE_HINT_DEFAULT, | 207 : ResourceProvider::TEXTURE_HINT_DEFAULT, |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 239 if (software_compositor) | 262 if (software_compositor) |
| 240 return coded_size; | 263 return coded_size; |
| 241 | 264 |
| 242 int plane_width = media::VideoFrame::Columns( | 265 int plane_width = media::VideoFrame::Columns( |
| 243 plane_index, input_frame->format(), coded_size.width()); | 266 plane_index, input_frame->format(), coded_size.width()); |
| 244 int plane_height = media::VideoFrame::Rows(plane_index, input_frame->format(), | 267 int plane_height = media::VideoFrame::Rows(plane_index, input_frame->format(), |
| 245 coded_size.height()); | 268 coded_size.height()); |
| 246 return gfx::Size(plane_width, plane_height); | 269 return gfx::Size(plane_width, plane_height); |
| 247 } | 270 } |
| 248 | 271 |
| 272 // Create an RGB texture by software converting YUV planar data, for the case | |
| 273 // where YUV planar textures are not renderable by the GPU. | |
| 274 VideoFrameExternalResources | |
| 275 VideoResourceUpdater::CreateRGBTextureForSoftwarePlanes( | |
| 276 media::VideoFrame* video_frame) { | |
| 277 DCHECK(media::IsYuvPlanar(video_frame->format())); | |
| 278 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); | |
| 279 const ResourceFormat resource_format = ResourceFormat::RGBA_8888; | |
| 280 const gfx::Size output_plane_resource_size = video_frame->coded_size(); | |
| 281 const bool is_immutable = false; | |
| 282 | |
| 283 VideoResourceUpdater::ResourceList::iterator resource = | |
| 284 RecycleOrAllocateTexture(output_plane_resource_size, resource_format, | |
| 285 is_immutable); | |
| 286 | |
| 287 resource->add_ref(); | |
| 288 | |
| 289 size_t bytes_per_row = ResourceUtil::CheckedWidthInBytes<size_t>( | |
| 290 output_plane_resource_size.width(), ResourceFormat::RGBA_8888); | |
| 291 size_t upload_image_stride = | |
| 292 MathUtil::CheckedRoundUp<size_t>(bytes_per_row, 4u); | |
|
danakj
2016/08/25 01:25:39
If the format is RGBA8888 how can the bytes per ro
Tobias Sargeant
2016/08/26 15:52:11
True, removed.
| |
| 293 size_t needed_size = | |
| 294 upload_image_stride * output_plane_resource_size.height(); | |
| 295 if (upload_pixels_.size() < needed_size) | |
| 296 upload_pixels_.resize(needed_size); | |
| 297 | |
| 298 media::SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels( | |
| 299 video_frame, &upload_pixels_[0], upload_image_stride); | |
| 300 | |
| 301 resource_provider_->CopyToResource( | |
| 302 resource->resource_id(), &upload_pixels_[0], resource->resource_size()); | |
| 303 | |
| 304 gpu::SyncToken sync_token; | |
| 305 const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM(); | |
| 306 gl->ShallowFlushCHROMIUM(); | |
| 307 gl->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); | |
| 308 | |
| 309 VideoFrameExternalResources external_resources; | |
| 310 | |
| 311 external_resources.mailboxes.push_back( | |
| 312 TextureMailbox(resource->mailbox(), sync_token, GL_TEXTURE_2D, | |
| 313 video_frame->coded_size(), false, false)); | |
| 314 external_resources.release_callbacks.push_back( | |
| 315 base::Bind(&RecycleResource, AsWeakPtr(), resource->resource_id())); | |
| 316 external_resources.type = VideoFrameExternalResources::RGBA_RESOURCE; | |
| 317 return external_resources; | |
| 318 } | |
| 319 | |
| 249 VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( | 320 VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( |
| 250 scoped_refptr<media::VideoFrame> video_frame) { | 321 scoped_refptr<media::VideoFrame> video_frame) { |
| 251 TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForSoftwarePlanes"); | 322 TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForSoftwarePlanes"); |
| 252 const media::VideoPixelFormat input_frame_format = video_frame->format(); | 323 const media::VideoPixelFormat input_frame_format = video_frame->format(); |
| 253 | 324 |
| 254 // TODO(hubbe): Make this a video frame method. | 325 // TODO(hubbe): Make this a video frame method. |
| 255 int bits_per_channel = 0; | 326 int bits_per_channel = 0; |
| 256 switch (input_frame_format) { | 327 switch (input_frame_format) { |
| 257 case media::PIXEL_FORMAT_UNKNOWN: | 328 case media::PIXEL_FORMAT_UNKNOWN: |
| 258 NOTREACHED(); | 329 NOTREACHED(); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 290 if (!media::IsYuvPlanar(input_frame_format)) { | 361 if (!media::IsYuvPlanar(input_frame_format)) { |
| 291 NOTREACHED() << media::VideoPixelFormatToString(input_frame_format); | 362 NOTREACHED() << media::VideoPixelFormatToString(input_frame_format); |
| 292 return VideoFrameExternalResources(); | 363 return VideoFrameExternalResources(); |
| 293 } | 364 } |
| 294 | 365 |
| 295 const bool software_compositor = context_provider_ == NULL; | 366 const bool software_compositor = context_provider_ == NULL; |
| 296 | 367 |
| 297 ResourceFormat output_resource_format = | 368 ResourceFormat output_resource_format = |
| 298 resource_provider_->YuvResourceFormat(bits_per_channel); | 369 resource_provider_->YuvResourceFormat(bits_per_channel); |
| 299 | 370 |
| 371 // If GPU compositing is enabled, but the output resource format | |
| 372 // returned by the resource provider is RGBA_8888, then a GPU driver | |
| 373 // bug workaround requires that YUV frames must be converted to RGB | |
| 374 // before texture upload. | |
| 375 if (!software_compositor && | |
| 376 output_resource_format == ResourceFormat::RGBA_8888) { | |
| 377 return CreateRGBTextureForSoftwarePlanes(video_frame.get()); | |
| 378 } | |
| 379 | |
| 300 size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_format); | 380 size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_format); |
| 301 | 381 |
| 302 // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB | 382 // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB |
| 303 // conversion here. That involves an extra copy of each frame to a bitmap. | 383 // conversion here. That involves an extra copy of each frame to a bitmap. |
| 304 // Obviously, this is suboptimal and should be addressed once ubercompositor | 384 // Obviously, this is suboptimal and should be addressed once ubercompositor |
| 305 // starts shaping up. | 385 // starts shaping up. |
| 306 if (software_compositor) { | 386 if (software_compositor) { |
| 307 output_resource_format = kRGBResourceFormat; | 387 output_resource_format = kRGBResourceFormat; |
| 308 output_plane_count = 1; | 388 output_plane_count = 1; |
| 309 } | 389 } |
| 310 | 390 |
| 311 // Drop recycled resources that are the wrong format. | 391 // Drop recycled resources that are the wrong format. |
|
danakj
2016/08/25 01:25:39
You're skipping code like this. I would have expec
| |
| 312 for (auto it = all_resources_.begin(); it != all_resources_.end();) { | 392 for (auto it = all_resources_.begin(); it != all_resources_.end();) { |
| 313 if (!it->has_refs() && it->resource_format() != output_resource_format) | 393 if (!it->has_refs() && it->resource_format() != output_resource_format) |
| 314 DeleteResource(it++); | 394 DeleteResource(it++); |
| 315 else | 395 else |
| 316 ++it; | 396 ++it; |
| 317 } | 397 } |
| 318 | 398 |
| 319 const int max_resource_size = resource_provider_->max_texture_size(); | 399 const int max_resource_size = resource_provider_->max_texture_size(); |
| 320 std::vector<ResourceList::iterator> plane_resources; | 400 std::vector<ResourceList::iterator> plane_resources; |
| 321 for (size_t i = 0; i < output_plane_count; ++i) { | 401 for (size_t i = 0; i < output_plane_count; ++i) { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 381 | 461 |
| 382 if (software_compositor) { | 462 if (software_compositor) { |
| 383 DCHECK_EQ(plane_resources.size(), 1u); | 463 DCHECK_EQ(plane_resources.size(), 1u); |
| 384 PlaneResource& plane_resource = *plane_resources[0]; | 464 PlaneResource& plane_resource = *plane_resources[0]; |
| 385 DCHECK_EQ(plane_resource.resource_format(), kRGBResourceFormat); | 465 DCHECK_EQ(plane_resource.resource_format(), kRGBResourceFormat); |
| 386 DCHECK(plane_resource.mailbox().IsZero()); | 466 DCHECK(plane_resource.mailbox().IsZero()); |
| 387 | 467 |
| 388 if (!plane_resource.Matches(video_frame->unique_id(), 0)) { | 468 if (!plane_resource.Matches(video_frame->unique_id(), 0)) { |
| 389 // We need to transfer data from |video_frame| to the plane resource. | 469 // We need to transfer data from |video_frame| to the plane resource. |
| 390 if (!video_renderer_) | 470 if (!video_renderer_) |
| 391 video_renderer_.reset(new media::SkCanvasVideoRenderer); | 471 video_renderer_.reset(new media::SkCanvasVideoRenderer); |
|
danakj
2016/08/25 01:25:39
Why are you using a diff skia thing to convert yuv
Tobias Sargeant
2016/08/26 15:52:11
I've reworked this to keep more of the original co
| |
| 392 | 472 |
| 393 ResourceProvider::ScopedWriteLockSoftware lock( | 473 ResourceProvider::ScopedWriteLockSoftware lock( |
| 394 resource_provider_, plane_resource.resource_id()); | 474 resource_provider_, plane_resource.resource_id()); |
| 395 SkCanvas canvas(lock.sk_bitmap()); | 475 SkCanvas canvas(lock.sk_bitmap()); |
| 396 // This is software path, so canvas and video_frame are always backed | 476 // This is software path, so canvas and video_frame are always backed |
| 397 // by software. | 477 // by software. |
| 398 video_renderer_->Copy(video_frame, &canvas, media::Context3D()); | 478 video_renderer_->Copy(video_frame, &canvas, media::Context3D()); |
|
danakj
2016/08/25 01:25:39
Then you could pass a context here when !software_
| |
| 399 plane_resource.SetUniqueId(video_frame->unique_id(), 0); | 479 plane_resource.SetUniqueId(video_frame->unique_id(), 0); |
|
danakj
2016/08/25 01:25:39
And then you don't miss important stuff like this.
| |
| 400 } | 480 } |
| 401 | 481 |
| 402 external_resources.software_resources.push_back( | 482 external_resources.software_resources.push_back( |
| 403 plane_resource.resource_id()); | 483 plane_resource.resource_id()); |
| 404 external_resources.software_release_callback = | 484 external_resources.software_release_callback = |
| 405 base::Bind(&RecycleResource, AsWeakPtr(), plane_resource.resource_id()); | 485 base::Bind(&RecycleResource, AsWeakPtr(), plane_resource.resource_id()); |
| 406 external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; | 486 external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; |
| 407 return external_resources; | 487 return external_resources; |
| 408 } | 488 } |
| 409 | 489 |
| 410 for (size_t i = 0; i < plane_resources.size(); ++i) { | 490 for (size_t i = 0; i < plane_resources.size(); ++i) { |
| 411 PlaneResource& plane_resource = *plane_resources[i]; | 491 PlaneResource& plane_resource = *plane_resources[i]; |
| 412 // Update each plane's resource id with its content. | 492 // Update each plane's resource id with its content. |
| 413 DCHECK_EQ(plane_resource.resource_format(), | 493 DCHECK_EQ(plane_resource.resource_format(), |
| 414 resource_provider_->YuvResourceFormat(bits_per_channel)); | 494 resource_provider_->YuvResourceFormat(bits_per_channel)); |
| 415 | 495 |
| 416 if (!plane_resource.Matches(video_frame->unique_id(), i)) { | 496 if (!plane_resource.Matches(video_frame->unique_id(), i)) { |
| 417 // We need to transfer data from |video_frame| to the plane resource. | 497 // We need to transfer data from |video_frame| to the plane resource. |
| 418 // TODO(reveman): Can use GpuMemoryBuffers here to improve performance. | 498 // TODO(reveman): Can use GpuMemoryBuffers here to improve performance. |
| 419 | 499 |
| 420 // The |resource_size_pixels| is the size of the resource we want to | 500 // The |resource_size_pixels| is the size of the resource we want to |
| 421 // upload to. | 501 // upload to. |
| 422 gfx::Size resource_size_pixels = plane_resource.resource_size(); | 502 gfx::Size resource_size_pixels = plane_resource.resource_size(); |
| 423 // The |video_stride_bytes| is the width of the video frame we are | 503 // The |video_stride_bytes| is the width of the video frame we are |
| 424 // uploading (including non-frame data to fill in the stride). | 504 // uploading (including non-frame data to fill in the stride). |
| 425 int video_stride_bytes = video_frame->stride(i); | 505 int video_stride_bytes = video_frame->stride(i); |
| 426 | 506 |
| 427 size_t bytes_per_row = ResourceUtil::UncheckedWidthInBytes<size_t>( | 507 size_t bytes_per_row = ResourceUtil::CheckedWidthInBytes<size_t>( |
| 428 resource_size_pixels.width(), plane_resource.resource_format()); | 508 resource_size_pixels.width(), plane_resource.resource_format()); |
| 429 // Use 4-byte row alignment (OpenGL default) for upload performance. | 509 // Use 4-byte row alignment (OpenGL default) for upload performance. |
| 430 // Assuming that GL_UNPACK_ALIGNMENT has not changed from default. | 510 // Assuming that GL_UNPACK_ALIGNMENT has not changed from default. |
| 431 size_t upload_image_stride = | 511 size_t upload_image_stride = |
| 432 MathUtil::UncheckedRoundUp<size_t>(bytes_per_row, 4u); | 512 MathUtil::CheckedRoundUp<size_t>(bytes_per_row, 4u); |
| 433 | 513 |
| 434 bool needs_conversion = false; | 514 bool needs_conversion = false; |
| 435 int shift = 0; | 515 int shift = 0; |
| 436 | 516 |
| 437 // LUMINANCE_F16 uses half-floats, so we always need a conversion step. | 517 // LUMINANCE_F16 uses half-floats, so we always need a conversion step. |
| 438 if (plane_resource.resource_format() == LUMINANCE_F16) { | 518 if (plane_resource.resource_format() == LUMINANCE_F16) { |
| 439 needs_conversion = true; | 519 needs_conversion = true; |
| 440 // Note that the current method of converting integers to half-floats | 520 // Note that the current method of converting integers to half-floats |
| 441 // stops working if you have more than 10 bits of data. | 521 // stops working if you have more than 10 bits of data. |
| 442 DCHECK_LE(bits_per_channel, 10); | 522 DCHECK_LE(bits_per_channel, 10); |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 557 const gpu::MailboxHolder& mailbox_holder, | 637 const gpu::MailboxHolder& mailbox_holder, |
| 558 VideoFrameExternalResources* external_resources) { | 638 VideoFrameExternalResources* external_resources) { |
| 559 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); | 639 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); |
| 560 SyncTokenClientImpl client(gl, mailbox_holder.sync_token); | 640 SyncTokenClientImpl client(gl, mailbox_holder.sync_token); |
| 561 | 641 |
| 562 const gfx::Size output_plane_resource_size = video_frame->coded_size(); | 642 const gfx::Size output_plane_resource_size = video_frame->coded_size(); |
| 563 // The copy needs to be a direct transfer of pixel data, so we use an RGBA8 | 643 // The copy needs to be a direct transfer of pixel data, so we use an RGBA8 |
| 564 // target to avoid loss of precision or dropping any alpha component. | 644 // target to avoid loss of precision or dropping any alpha component. |
| 565 const ResourceFormat copy_target_format = ResourceFormat::RGBA_8888; | 645 const ResourceFormat copy_target_format = ResourceFormat::RGBA_8888; |
| 566 | 646 |
| 567 // Search for an existing resource to reuse. | 647 const bool is_immutable = false; |
| 568 VideoResourceUpdater::ResourceList::iterator resource = all_resources_.end(); | 648 VideoResourceUpdater::ResourceList::iterator resource = |
| 569 | 649 RecycleOrAllocateTexture(output_plane_resource_size, copy_target_format, |
| 570 for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) { | 650 video_frame->ColorSpace(), is_immutable); |
| 571 // Reuse resource if attributes match and the resource is a currently | |
| 572 // unreferenced texture. | |
| 573 if (it->resource_size() == output_plane_resource_size && | |
| 574 it->resource_format() == copy_target_format && | |
| 575 !it->mailbox().IsZero() && !it->has_refs() && | |
| 576 resource_provider_->GetTextureHint(it->resource_id()) != | |
| 577 ResourceProvider::TEXTURE_HINT_IMMUTABLE) { | |
| 578 resource = it; | |
| 579 break; | |
| 580 } | |
| 581 } | |
| 582 | |
| 583 // Otherwise allocate a new resource. | |
| 584 if (resource == all_resources_.end()) { | |
| 585 const bool is_immutable = false; | |
| 586 resource = AllocateResource(output_plane_resource_size, copy_target_format, | |
| 587 video_frame->ColorSpace(), true, is_immutable); | |
| 588 } | |
| 589 | 651 |
| 590 resource->add_ref(); | 652 resource->add_ref(); |
| 591 | 653 |
| 592 ResourceProvider::ScopedWriteLockGL lock(resource_provider_, | 654 ResourceProvider::ScopedWriteLockGL lock(resource_provider_, |
| 593 resource->resource_id(), false); | 655 resource->resource_id(), false); |
| 594 DCHECK_EQ( | 656 DCHECK_EQ( |
| 595 resource_provider_->GetResourceTextureTarget(resource->resource_id()), | 657 resource_provider_->GetResourceTextureTarget(resource->resource_id()), |
| 596 (GLenum)GL_TEXTURE_2D); | 658 (GLenum)GL_TEXTURE_2D); |
| 597 | 659 |
| 598 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); | 660 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 696 if (lost_resource) { | 758 if (lost_resource) { |
| 697 resource_it->clear_refs(); | 759 resource_it->clear_refs(); |
| 698 updater->DeleteResource(resource_it); | 760 updater->DeleteResource(resource_it); |
| 699 return; | 761 return; |
| 700 } | 762 } |
| 701 | 763 |
| 702 resource_it->remove_ref(); | 764 resource_it->remove_ref(); |
| 703 } | 765 } |
| 704 | 766 |
| 705 } // namespace cc | 767 } // namespace cc |
| OLD | NEW |