Revert 279472 "Mojo: Add mojo_shell_tests to the Linux bots."
[chromium-blink-merge.git] / cc / resources / video_resource_updater.cc
blob1d93aebe3cce20f763da7349490d0f2a3f0d177b
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 "base/bind.h"
8 #include "cc/output/gl_renderer.h"
9 #include "cc/resources/resource_provider.h"
10 #include "gpu/GLES2/gl2extchromium.h"
11 #include "gpu/command_buffer/client/gles2_interface.h"
12 #include "media/base/video_frame.h"
13 #include "media/filters/skcanvas_video_renderer.h"
14 #include "third_party/khronos/GLES2/gl2.h"
15 #include "third_party/khronos/GLES2/gl2ext.h"
16 #include "ui/gfx/size_conversions.h"
18 namespace cc {
20 const ResourceFormat kYUVResourceFormat = LUMINANCE_8;
21 const ResourceFormat kRGBResourceFormat = RGBA_8888;
23 VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {}
25 VideoFrameExternalResources::~VideoFrameExternalResources() {}
27 VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider,
28 ResourceProvider* resource_provider)
29 : context_provider_(context_provider),
30 resource_provider_(resource_provider) {
33 VideoResourceUpdater::~VideoResourceUpdater() {
34 while (!all_resources_.empty()) {
35 resource_provider_->DeleteResource(all_resources_.back());
36 all_resources_.pop_back();
40 void VideoResourceUpdater::DeleteResource(unsigned resource_id) {
41 resource_provider_->DeleteResource(resource_id);
42 all_resources_.erase(std::remove(all_resources_.begin(),
43 all_resources_.end(),
44 resource_id));
47 VideoFrameExternalResources VideoResourceUpdater::
48 CreateExternalResourcesFromVideoFrame(
49 const scoped_refptr<media::VideoFrame>& video_frame) {
50 if (!VerifyFrame(video_frame))
51 return VideoFrameExternalResources();
53 if (video_frame->format() == media::VideoFrame::NATIVE_TEXTURE)
54 return CreateForHardwarePlanes(video_frame);
55 else
56 return CreateForSoftwarePlanes(video_frame);
59 bool VideoResourceUpdater::VerifyFrame(
60 const scoped_refptr<media::VideoFrame>& video_frame) {
61 switch (video_frame->format()) {
62 // Acceptable inputs.
63 case media::VideoFrame::YV12:
64 case media::VideoFrame::I420:
65 case media::VideoFrame::YV12A:
66 case media::VideoFrame::YV16:
67 case media::VideoFrame::YV12J:
68 case media::VideoFrame::YV24:
69 case media::VideoFrame::NATIVE_TEXTURE:
70 #if defined(VIDEO_HOLE)
71 case media::VideoFrame::HOLE:
72 #endif // defined(VIDEO_HOLE)
73 return true;
75 // Unacceptable inputs. ¯\(°_o)/¯
76 case media::VideoFrame::UNKNOWN:
77 case media::VideoFrame::NV12:
78 break;
80 return false;
83 // For frames that we receive in software format, determine the dimensions of
84 // each plane in the frame.
85 static gfx::Size SoftwarePlaneDimension(
86 const scoped_refptr<media::VideoFrame>& input_frame,
87 ResourceFormat output_resource_format,
88 size_t plane_index) {
89 if (output_resource_format == kYUVResourceFormat) {
90 return media::VideoFrame::PlaneSize(
91 input_frame->format(), plane_index, input_frame->coded_size());
94 DCHECK_EQ(output_resource_format, kRGBResourceFormat);
95 return input_frame->coded_size();
98 VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
99 const scoped_refptr<media::VideoFrame>& video_frame) {
100 media::VideoFrame::Format input_frame_format = video_frame->format();
102 #if defined(VIDEO_HOLE)
103 if (input_frame_format == media::VideoFrame::HOLE) {
104 VideoFrameExternalResources external_resources;
105 external_resources.type = VideoFrameExternalResources::HOLE;
106 return external_resources;
108 #endif // defined(VIDEO_HOLE)
110 // Only YUV software video frames are supported.
111 DCHECK(input_frame_format == media::VideoFrame::YV12 ||
112 input_frame_format == media::VideoFrame::I420 ||
113 input_frame_format == media::VideoFrame::YV12A ||
114 input_frame_format == media::VideoFrame::YV12J ||
115 input_frame_format == media::VideoFrame::YV16 ||
116 input_frame_format == media::VideoFrame::YV24);
117 if (input_frame_format != media::VideoFrame::YV12 &&
118 input_frame_format != media::VideoFrame::I420 &&
119 input_frame_format != media::VideoFrame::YV12A &&
120 input_frame_format != media::VideoFrame::YV12J &&
121 input_frame_format != media::VideoFrame::YV16 &&
122 input_frame_format != media::VideoFrame::YV24)
123 return VideoFrameExternalResources();
125 bool software_compositor = context_provider_ == NULL;
127 ResourceFormat output_resource_format = kYUVResourceFormat;
128 size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_format);
130 // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB
131 // conversion here. That involves an extra copy of each frame to a bitmap.
132 // Obviously, this is suboptimal and should be addressed once ubercompositor
133 // starts shaping up.
134 if (software_compositor) {
135 output_resource_format = kRGBResourceFormat;
136 output_plane_count = 1;
139 int max_resource_size = resource_provider_->max_texture_size();
140 std::vector<PlaneResource> plane_resources;
141 bool allocation_success = true;
143 for (size_t i = 0; i < output_plane_count; ++i) {
144 gfx::Size output_plane_resource_size =
145 SoftwarePlaneDimension(video_frame, output_resource_format, i);
146 if (output_plane_resource_size.IsEmpty() ||
147 output_plane_resource_size.width() > max_resource_size ||
148 output_plane_resource_size.height() > max_resource_size) {
149 allocation_success = false;
150 break;
153 ResourceProvider::ResourceId resource_id = 0;
154 gpu::Mailbox mailbox;
156 // Try recycle a previously-allocated resource.
157 for (size_t i = 0; i < recycled_resources_.size(); ++i) {
158 bool resource_matches =
159 recycled_resources_[i].resource_format == output_resource_format &&
160 recycled_resources_[i].resource_size == output_plane_resource_size;
161 bool not_in_use =
162 !software_compositor || !resource_provider_->InUseByConsumer(
163 recycled_resources_[i].resource_id);
164 if (resource_matches && not_in_use) {
165 resource_id = recycled_resources_[i].resource_id;
166 mailbox = recycled_resources_[i].mailbox;
167 recycled_resources_.erase(recycled_resources_.begin() + i);
168 break;
172 if (resource_id == 0) {
173 // TODO(danakj): Abstract out hw/sw resource create/delete from
174 // ResourceProvider and stop using ResourceProvider in this class.
175 resource_id =
176 resource_provider_->CreateResource(output_plane_resource_size,
177 GL_CLAMP_TO_EDGE,
178 ResourceProvider::TextureUsageAny,
179 output_resource_format);
181 DCHECK(mailbox.IsZero());
183 if (!software_compositor) {
184 DCHECK(context_provider_);
186 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
188 GLC(gl, gl->GenMailboxCHROMIUM(mailbox.name));
189 ResourceProvider::ScopedWriteLockGL lock(resource_provider_,
190 resource_id);
191 GLC(gl, gl->BindTexture(GL_TEXTURE_2D, lock.texture_id()));
192 GLC(gl, gl->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name));
193 GLC(gl, gl->BindTexture(GL_TEXTURE_2D, 0));
196 if (resource_id)
197 all_resources_.push_back(resource_id);
200 if (resource_id == 0) {
201 allocation_success = false;
202 break;
205 DCHECK(software_compositor || !mailbox.IsZero());
206 plane_resources.push_back(PlaneResource(resource_id,
207 output_plane_resource_size,
208 output_resource_format,
209 mailbox));
212 if (!allocation_success) {
213 for (size_t i = 0; i < plane_resources.size(); ++i)
214 DeleteResource(plane_resources[i].resource_id);
215 return VideoFrameExternalResources();
218 VideoFrameExternalResources external_resources;
220 if (software_compositor) {
221 DCHECK_EQ(plane_resources.size(), 1u);
222 DCHECK_EQ(plane_resources[0].resource_format, kRGBResourceFormat);
223 DCHECK(plane_resources[0].mailbox.IsZero());
225 if (!video_renderer_)
226 video_renderer_.reset(new media::SkCanvasVideoRenderer);
229 ResourceProvider::ScopedWriteLockSoftware lock(
230 resource_provider_, plane_resources[0].resource_id);
231 video_renderer_->Paint(video_frame.get(),
232 lock.sk_canvas(),
233 video_frame->visible_rect(),
234 0xff);
237 RecycleResourceData recycle_data = {
238 plane_resources[0].resource_id,
239 plane_resources[0].resource_size,
240 plane_resources[0].resource_format,
241 gpu::Mailbox()
243 external_resources.software_resources.push_back(
244 plane_resources[0].resource_id);
245 external_resources.software_release_callback =
246 base::Bind(&RecycleResource, AsWeakPtr(), recycle_data);
247 external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE;
249 return external_resources;
252 for (size_t i = 0; i < plane_resources.size(); ++i) {
253 // Update each plane's resource id with its content.
254 DCHECK_EQ(plane_resources[i].resource_format, kYUVResourceFormat);
256 const uint8_t* input_plane_pixels = video_frame->data(i);
258 gfx::Rect image_rect(0,
260 video_frame->stride(i),
261 plane_resources[i].resource_size.height());
262 gfx::Rect source_rect(plane_resources[i].resource_size);
263 resource_provider_->SetPixels(plane_resources[i].resource_id,
264 input_plane_pixels,
265 image_rect,
266 source_rect,
267 gfx::Vector2d());
269 RecycleResourceData recycle_data = {
270 plane_resources[i].resource_id,
271 plane_resources[i].resource_size,
272 plane_resources[i].resource_format,
273 plane_resources[i].mailbox
276 external_resources.mailboxes.push_back(
277 TextureMailbox(plane_resources[i].mailbox, GL_TEXTURE_2D, 0));
278 external_resources.release_callbacks.push_back(
279 base::Bind(&RecycleResource, AsWeakPtr(), recycle_data));
282 external_resources.type = VideoFrameExternalResources::YUV_RESOURCE;
283 return external_resources;
286 static void ReturnTexture(const scoped_refptr<media::VideoFrame>& frame,
287 uint32 sync_point,
288 bool lost_resource) {
289 frame->AppendReleaseSyncPoint(sync_point);
292 VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes(
293 const scoped_refptr<media::VideoFrame>& video_frame) {
294 media::VideoFrame::Format frame_format = video_frame->format();
296 DCHECK_EQ(frame_format, media::VideoFrame::NATIVE_TEXTURE);
297 if (frame_format != media::VideoFrame::NATIVE_TEXTURE)
298 return VideoFrameExternalResources();
300 if (!context_provider_)
301 return VideoFrameExternalResources();
303 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
304 VideoFrameExternalResources external_resources;
305 switch (mailbox_holder->texture_target) {
306 case GL_TEXTURE_2D:
307 external_resources.type = VideoFrameExternalResources::RGB_RESOURCE;
308 break;
309 case GL_TEXTURE_EXTERNAL_OES:
310 external_resources.type =
311 VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE;
312 break;
313 case GL_TEXTURE_RECTANGLE_ARB:
314 external_resources.type = VideoFrameExternalResources::IO_SURFACE;
315 break;
316 default:
317 NOTREACHED();
318 return VideoFrameExternalResources();
321 external_resources.mailboxes.push_back(
322 TextureMailbox(mailbox_holder->mailbox,
323 mailbox_holder->texture_target,
324 mailbox_holder->sync_point));
325 external_resources.release_callbacks.push_back(
326 base::Bind(&ReturnTexture, video_frame));
327 return external_resources;
330 // static
331 void VideoResourceUpdater::RecycleResource(
332 base::WeakPtr<VideoResourceUpdater> updater,
333 RecycleResourceData data,
334 uint32 sync_point,
335 bool lost_resource) {
336 if (!updater.get()) {
337 // Resource was already deleted.
338 return;
341 ContextProvider* context_provider = updater->context_provider_;
342 if (context_provider && sync_point) {
343 GLC(context_provider->ContextGL(),
344 context_provider->ContextGL()->WaitSyncPointCHROMIUM(sync_point));
347 if (lost_resource) {
348 updater->DeleteResource(data.resource_id);
349 return;
352 // Drop recycled resources that are the wrong format.
353 while (!updater->recycled_resources_.empty() &&
354 updater->recycled_resources_.back().resource_format !=
355 data.resource_format) {
356 updater->DeleteResource(updater->recycled_resources_.back().resource_id);
357 updater->recycled_resources_.pop_back();
360 PlaneResource recycled_resource(data.resource_id,
361 data.resource_size,
362 data.resource_format,
363 data.mailbox);
364 updater->recycled_resources_.push_back(recycled_resource);
367 } // namespace cc