Requiem for client_screen.js
[chromium-blink-merge.git] / cc / resources / video_resource_updater.cc
blobed57e6fb9edbf95f7b4277b868f8dce2fe8969ec
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
3 // found in the LICENSE file.
5 #include "cc/resources/video_resource_updater.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/trace_event/trace_event.h"
11 #include "cc/base/util.h"
12 #include "cc/output/gl_renderer.h"
13 #include "cc/resources/resource_provider.h"
14 #include "gpu/GLES2/gl2extchromium.h"
15 #include "gpu/command_buffer/client/gles2_interface.h"
16 #include "media/base/video_frame.h"
17 #include "media/blink/skcanvas_video_renderer.h"
18 #include "third_party/khronos/GLES2/gl2.h"
19 #include "third_party/khronos/GLES2/gl2ext.h"
20 #include "ui/gfx/geometry/size_conversions.h"
22 namespace cc {
24 namespace {
26 const ResourceFormat kRGBResourceFormat = RGBA_8888;
28 class SyncPointClientImpl : public media::VideoFrame::SyncPointClient {
29 public:
30 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
31 ~SyncPointClientImpl() override {}
32 uint32 InsertSyncPoint() override {
33 return GLC(gl_, gl_->InsertSyncPointCHROMIUM());
35 void WaitSyncPoint(uint32 sync_point) override {
36 GLC(gl_, gl_->WaitSyncPointCHROMIUM(sync_point));
39 private:
40 gpu::gles2::GLES2Interface* gl_;
43 } // namespace
45 VideoResourceUpdater::PlaneResource::PlaneResource(
46 unsigned int resource_id,
47 const gfx::Size& resource_size,
48 ResourceFormat resource_format,
49 gpu::Mailbox mailbox)
50 : resource_id(resource_id),
51 resource_size(resource_size),
52 resource_format(resource_format),
53 mailbox(mailbox),
54 ref_count(0),
55 frame_ptr(nullptr),
56 plane_index(0) {
59 bool VideoResourceUpdater::PlaneResourceMatchesUniqueID(
60 const PlaneResource& plane_resource,
61 const media::VideoFrame* video_frame,
62 int plane_index) {
63 return plane_resource.frame_ptr == video_frame &&
64 plane_resource.plane_index == plane_index &&
65 plane_resource.timestamp == video_frame->timestamp();
68 void VideoResourceUpdater::SetPlaneResourceUniqueId(
69 const media::VideoFrame* video_frame,
70 int plane_index,
71 PlaneResource* plane_resource) {
72 plane_resource->frame_ptr = video_frame;
73 plane_resource->plane_index = plane_index;
74 plane_resource->timestamp = video_frame->timestamp();
77 VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {}
79 VideoFrameExternalResources::~VideoFrameExternalResources() {}
81 VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider,
82 ResourceProvider* resource_provider)
83 : context_provider_(context_provider),
84 resource_provider_(resource_provider) {
87 VideoResourceUpdater::~VideoResourceUpdater() {
88 for (const PlaneResource& plane_resource : all_resources_)
89 resource_provider_->DeleteResource(plane_resource.resource_id);
92 VideoResourceUpdater::ResourceList::iterator
93 VideoResourceUpdater::AllocateResource(const gfx::Size& plane_size,
94 ResourceFormat format,
95 bool has_mailbox) {
96 // TODO(danakj): Abstract out hw/sw resource create/delete from
97 // ResourceProvider and stop using ResourceProvider in this class.
98 const ResourceProvider::ResourceId resource_id =
99 resource_provider_->CreateResource(
100 plane_size, GL_CLAMP_TO_EDGE,
101 ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
102 if (resource_id == 0)
103 return all_resources_.end();
105 gpu::Mailbox mailbox;
106 if (has_mailbox) {
107 DCHECK(context_provider_);
109 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
111 GLC(gl, gl->GenMailboxCHROMIUM(mailbox.name));
112 ResourceProvider::ScopedWriteLockGL lock(resource_provider_, resource_id);
113 GLC(gl, gl->ProduceTextureDirectCHROMIUM(lock.texture_id(), GL_TEXTURE_2D,
114 mailbox.name));
116 all_resources_.push_front(
117 PlaneResource(resource_id, plane_size, format, mailbox));
118 return all_resources_.begin();
121 void VideoResourceUpdater::DeleteResource(ResourceList::iterator resource_it) {
122 DCHECK_EQ(resource_it->ref_count, 0);
123 resource_provider_->DeleteResource(resource_it->resource_id);
124 all_resources_.erase(resource_it);
127 VideoFrameExternalResources VideoResourceUpdater::
128 CreateExternalResourcesFromVideoFrame(
129 const scoped_refptr<media::VideoFrame>& video_frame) {
130 if (!VerifyFrame(video_frame))
131 return VideoFrameExternalResources();
133 if (video_frame->format() == media::VideoFrame::NATIVE_TEXTURE)
134 return CreateForHardwarePlanes(video_frame);
135 else
136 return CreateForSoftwarePlanes(video_frame);
139 bool VideoResourceUpdater::VerifyFrame(
140 const scoped_refptr<media::VideoFrame>& video_frame) {
141 switch (video_frame->format()) {
142 // Acceptable inputs.
143 case media::VideoFrame::YV12:
144 case media::VideoFrame::I420:
145 case media::VideoFrame::YV12A:
146 case media::VideoFrame::YV16:
147 case media::VideoFrame::YV12J:
148 case media::VideoFrame::YV12HD:
149 case media::VideoFrame::YV24:
150 case media::VideoFrame::NATIVE_TEXTURE:
151 #if defined(VIDEO_HOLE)
152 case media::VideoFrame::HOLE:
153 #endif // defined(VIDEO_HOLE)
154 case media::VideoFrame::ARGB:
155 return true;
157 // Unacceptable inputs. ¯\(°_o)/¯
158 case media::VideoFrame::UNKNOWN:
159 case media::VideoFrame::NV12:
160 break;
162 return false;
165 // For frames that we receive in software format, determine the dimensions of
166 // each plane in the frame.
167 static gfx::Size SoftwarePlaneDimension(
168 const scoped_refptr<media::VideoFrame>& input_frame,
169 bool software_compositor,
170 size_t plane_index) {
171 if (!software_compositor) {
172 return media::VideoFrame::PlaneSize(
173 input_frame->format(), plane_index, input_frame->coded_size());
175 return input_frame->coded_size();
178 VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
179 const scoped_refptr<media::VideoFrame>& video_frame) {
180 TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForSoftwarePlanes");
181 media::VideoFrame::Format input_frame_format = video_frame->format();
183 #if defined(VIDEO_HOLE)
184 if (input_frame_format == media::VideoFrame::HOLE) {
185 VideoFrameExternalResources external_resources;
186 external_resources.type = VideoFrameExternalResources::HOLE;
187 return external_resources;
189 #endif // defined(VIDEO_HOLE)
191 // Only YUV software video frames are supported.
192 if (input_frame_format != media::VideoFrame::YV12 &&
193 input_frame_format != media::VideoFrame::I420 &&
194 input_frame_format != media::VideoFrame::YV12A &&
195 input_frame_format != media::VideoFrame::YV12J &&
196 input_frame_format != media::VideoFrame::YV12HD &&
197 input_frame_format != media::VideoFrame::YV16 &&
198 input_frame_format != media::VideoFrame::YV24) {
199 NOTREACHED() << input_frame_format;
200 return VideoFrameExternalResources();
203 bool software_compositor = context_provider_ == NULL;
205 ResourceFormat output_resource_format =
206 resource_provider_->yuv_resource_format();
207 size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_format);
209 // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB
210 // conversion here. That involves an extra copy of each frame to a bitmap.
211 // Obviously, this is suboptimal and should be addressed once ubercompositor
212 // starts shaping up.
213 if (software_compositor) {
214 output_resource_format = kRGBResourceFormat;
215 output_plane_count = 1;
218 // Drop recycled resources that are the wrong format.
219 for (auto it = all_resources_.begin(); it != all_resources_.end();) {
220 if (it->ref_count == 0 && it->resource_format != output_resource_format)
221 DeleteResource(it++);
222 else
223 ++it;
226 const int max_resource_size = resource_provider_->max_texture_size();
227 std::vector<ResourceList::iterator> plane_resources;
228 for (size_t i = 0; i < output_plane_count; ++i) {
229 gfx::Size output_plane_resource_size =
230 SoftwarePlaneDimension(video_frame, software_compositor, i);
231 if (output_plane_resource_size.IsEmpty() ||
232 output_plane_resource_size.width() > max_resource_size ||
233 output_plane_resource_size.height() > max_resource_size) {
234 break;
237 // Try recycle a previously-allocated resource.
238 ResourceList::iterator resource_it = all_resources_.end();
239 for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) {
240 if (it->resource_size == output_plane_resource_size &&
241 it->resource_format == output_resource_format) {
242 if (PlaneResourceMatchesUniqueID(*it, video_frame.get(), i)) {
243 // Bingo, we found a resource that already contains the data we are
244 // planning to put in it. It's safe to reuse it even if
245 // resource_provider_ holds some references to it, because those
246 // references are read-only.
247 resource_it = it;
248 break;
251 // This extra check is needed because resources backed by SharedMemory
252 // are not ref-counted, unlike mailboxes. Full discussion in
253 // codereview.chromium.org/145273021.
254 const bool in_use =
255 software_compositor &&
256 resource_provider_->InUseByConsumer(it->resource_id);
257 if (it->ref_count == 0 && !in_use) {
258 // We found a resource with the correct size that we can overwrite.
259 resource_it = it;
264 // Check if we need to allocate a new resource.
265 if (resource_it == all_resources_.end()) {
266 resource_it =
267 AllocateResource(output_plane_resource_size, output_resource_format,
268 !software_compositor);
270 if (resource_it == all_resources_.end())
271 break;
273 ++resource_it->ref_count;
274 plane_resources.push_back(resource_it);
277 if (plane_resources.size() != output_plane_count) {
278 // Allocation failed, nothing will be returned so restore reference counts.
279 for (ResourceList::iterator resource_it : plane_resources)
280 --resource_it->ref_count;
281 return VideoFrameExternalResources();
284 VideoFrameExternalResources external_resources;
286 if (software_compositor) {
287 DCHECK_EQ(plane_resources.size(), 1u);
288 PlaneResource& plane_resource = *plane_resources[0];
289 DCHECK_EQ(plane_resource.resource_format, kRGBResourceFormat);
290 DCHECK(plane_resource.mailbox.IsZero());
292 if (!PlaneResourceMatchesUniqueID(plane_resource, video_frame.get(), 0)) {
293 // We need to transfer data from |video_frame| to the plane resource.
294 if (!video_renderer_)
295 video_renderer_.reset(new media::SkCanvasVideoRenderer);
297 ResourceProvider::ScopedWriteLockSoftware lock(
298 resource_provider_, plane_resource.resource_id);
299 SkCanvas canvas(lock.sk_bitmap());
300 // This is software path, so canvas and video_frame are always backed
301 // by software.
302 video_renderer_->Copy(video_frame, &canvas, media::Context3D());
303 SetPlaneResourceUniqueId(video_frame.get(), 0, &plane_resource);
306 external_resources.software_resources.push_back(plane_resource.resource_id);
307 external_resources.software_release_callback =
308 base::Bind(&RecycleResource, AsWeakPtr(), plane_resource.resource_id);
309 external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE;
310 return external_resources;
313 for (size_t i = 0; i < plane_resources.size(); ++i) {
314 PlaneResource& plane_resource = *plane_resources[i];
315 // Update each plane's resource id with its content.
316 DCHECK_EQ(plane_resource.resource_format,
317 resource_provider_->yuv_resource_format());
319 if (!PlaneResourceMatchesUniqueID(plane_resource, video_frame.get(), i)) {
320 // We need to transfer data from |video_frame| to the plane resource.
321 // TODO(reveman): Can use GpuMemoryBuffers here to improve performance.
323 // The |resource_size_pixels| is the size of the resource we want to
324 // upload to.
325 gfx::Size resource_size_pixels = plane_resource.resource_size;
326 // The |video_stride_pixels| is the width of the video frame we are
327 // uploading (including non-frame data to fill in the stride).
328 size_t video_stride_pixels = video_frame->stride(i);
330 size_t bytes_per_pixel = BitsPerPixel(plane_resource.resource_format) / 8;
331 // Use 4-byte row alignment (OpenGL default) for upload performance.
332 // Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
333 size_t upload_image_stride =
334 RoundUp<size_t>(bytes_per_pixel * resource_size_pixels.width(), 4u);
336 const uint8_t* pixels;
337 if (upload_image_stride == video_stride_pixels * bytes_per_pixel) {
338 pixels = video_frame->data(i);
339 } else {
340 // Avoid malloc for each frame/plane if possible.
341 size_t needed_size =
342 upload_image_stride * resource_size_pixels.height();
343 if (upload_pixels_.size() < needed_size)
344 upload_pixels_.resize(needed_size);
345 for (int row = 0; row < resource_size_pixels.height(); ++row) {
346 uint8_t* dst = &upload_pixels_[upload_image_stride * row];
347 const uint8_t* src = video_frame->data(i) +
348 bytes_per_pixel * video_stride_pixels * row;
349 memcpy(dst, src, resource_size_pixels.width() * bytes_per_pixel);
351 pixels = &upload_pixels_[0];
354 resource_provider_->CopyToResource(plane_resource.resource_id, pixels,
355 resource_size_pixels);
356 SetPlaneResourceUniqueId(video_frame.get(), i, &plane_resource);
359 external_resources.mailboxes.push_back(
360 TextureMailbox(plane_resource.mailbox, GL_TEXTURE_2D, 0));
361 external_resources.release_callbacks.push_back(
362 base::Bind(&RecycleResource, AsWeakPtr(), plane_resource.resource_id));
365 external_resources.type = VideoFrameExternalResources::YUV_RESOURCE;
366 return external_resources;
369 // static
370 void VideoResourceUpdater::ReturnTexture(
371 base::WeakPtr<VideoResourceUpdater> updater,
372 const scoped_refptr<media::VideoFrame>& video_frame,
373 uint32 sync_point,
374 bool lost_resource,
375 BlockingTaskRunner* main_thread_task_runner) {
376 // TODO(dshwang) this case should be forwarded to the decoder as lost
377 // resource.
378 if (lost_resource || !updater.get())
379 return;
380 // VideoFrame::UpdateReleaseSyncPoint() creates new sync point using the same
381 // GL context which created the given |sync_point|, so discard the
382 // |sync_point|.
383 SyncPointClientImpl client(updater->context_provider_->ContextGL());
384 video_frame->UpdateReleaseSyncPoint(&client);
387 VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes(
388 const scoped_refptr<media::VideoFrame>& video_frame) {
389 TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForHardwarePlanes");
390 media::VideoFrame::Format frame_format = video_frame->format();
392 DCHECK_EQ(frame_format, media::VideoFrame::NATIVE_TEXTURE);
393 if (frame_format != media::VideoFrame::NATIVE_TEXTURE)
394 return VideoFrameExternalResources();
396 if (!context_provider_)
397 return VideoFrameExternalResources();
399 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
400 VideoFrameExternalResources external_resources;
401 switch (mailbox_holder->texture_target) {
402 case GL_TEXTURE_2D:
403 external_resources.type = VideoFrameExternalResources::RGB_RESOURCE;
404 break;
405 case GL_TEXTURE_EXTERNAL_OES:
406 external_resources.type =
407 VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE;
408 break;
409 case GL_TEXTURE_RECTANGLE_ARB:
410 external_resources.type = VideoFrameExternalResources::IO_SURFACE;
411 break;
412 default:
413 NOTREACHED();
414 return VideoFrameExternalResources();
417 external_resources.mailboxes.push_back(
418 TextureMailbox(mailbox_holder->mailbox,
419 mailbox_holder->texture_target,
420 mailbox_holder->sync_point));
421 external_resources.mailboxes.back().set_allow_overlay(
422 video_frame->allow_overlay());
423 external_resources.release_callbacks.push_back(
424 base::Bind(&ReturnTexture, AsWeakPtr(), video_frame));
425 return external_resources;
428 // static
429 void VideoResourceUpdater::RecycleResource(
430 base::WeakPtr<VideoResourceUpdater> updater,
431 ResourceProvider::ResourceId resource_id,
432 uint32 sync_point,
433 bool lost_resource,
434 BlockingTaskRunner* main_thread_task_runner) {
435 if (!updater.get()) {
436 // Resource was already deleted.
437 return;
440 const ResourceList::iterator resource_it = std::find_if(
441 updater->all_resources_.begin(), updater->all_resources_.end(),
442 [resource_id](const PlaneResource& plane_resource) {
443 return plane_resource.resource_id == resource_id;
445 if (resource_it == updater->all_resources_.end())
446 return;
448 ContextProvider* context_provider = updater->context_provider_;
449 if (context_provider && sync_point) {
450 GLC(context_provider->ContextGL(),
451 context_provider->ContextGL()->WaitSyncPointCHROMIUM(sync_point));
454 if (lost_resource) {
455 resource_it->ref_count = 0;
456 updater->DeleteResource(resource_it);
457 return;
460 --resource_it->ref_count;
461 DCHECK_GE(resource_it->ref_count, 0);
464 } // namespace cc