Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(90)

Side by Side Diff: content/common/gpu/media/android_deferred_rendering_backing_strategy.cc

Issue 1882373004: Migrate content/common/gpu/media code to media/gpu (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Squash and rebase Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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.
4
5 #include "content/common/gpu/media/android_deferred_rendering_backing_strategy.h "
6
7 #include <EGL/egl.h>
8 #include <EGL/eglext.h>
9
10 #include "base/android/build_info.h"
11 #include "base/bind.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "content/common/gpu/media/avda_codec_image.h"
16 #include "content/common/gpu/media/avda_return_on_failure.h"
17 #include "content/common/gpu/media/avda_shared_state.h"
18 #include "gpu/command_buffer/service/context_group.h"
19 #include "gpu/command_buffer/service/gl_stream_texture_image.h"
20 #include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
21 #include "gpu/command_buffer/service/texture_manager.h"
22 #include "gpu/ipc/common/gpu_surface_lookup.h"
23 #include "gpu/ipc/service/gpu_channel.h"
24 #include "ui/gl/android/surface_texture.h"
25 #include "ui/gl/egl_util.h"
26 #include "ui/gl/gl_bindings.h"
27 #include "ui/gl/gl_surface_egl.h"
28 #include "ui/gl/scoped_binders.h"
29 #include "ui/gl/scoped_make_current.h"
30
31 namespace content {
32
33 AndroidDeferredRenderingBackingStrategy::
34 AndroidDeferredRenderingBackingStrategy(AVDAStateProvider* state_provider)
35 : state_provider_(state_provider), media_codec_(nullptr) {}
36
37 AndroidDeferredRenderingBackingStrategy::
38 ~AndroidDeferredRenderingBackingStrategy() {}
39
40 gfx::ScopedJavaSurface AndroidDeferredRenderingBackingStrategy::Initialize(
41 int surface_view_id) {
42 shared_state_ = new AVDASharedState();
43
44 // Create a texture for the SurfaceTexture to use. We don't attach it here
45 // so that it gets attached in the compositor gl context in the common case.
46 GLuint service_id = 0;
47 glGenTextures(1, &service_id);
48 DCHECK(service_id);
49 shared_state_->set_surface_texture_service_id(service_id);
50
51 gfx::ScopedJavaSurface surface;
52 if (surface_view_id != media::VideoDecodeAccelerator::Config::kNoSurfaceID) {
53 surface = gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface(
54 surface_view_id);
55 } else {
56 if (DoesSurfaceTextureDetachWork()) {
57 // Create a detached SurfaceTexture. Detaching it will silently fail to
58 // delete texture 0.
59 surface_texture_ = gfx::SurfaceTexture::Create(0);
60 surface_texture_->DetachFromGLContext();
61 } else {
62 // Detach doesn't work so well on all platforms. Just attach the
63 // SurfaceTexture here, and probably context switch later.
64 surface_texture_ = gfx::SurfaceTexture::Create(service_id);
65 shared_state_->DidAttachSurfaceTexture();
66 }
67 surface = gfx::ScopedJavaSurface(surface_texture_.get());
68 }
69
70 return surface;
71 }
72
73 void AndroidDeferredRenderingBackingStrategy::Cleanup(
74 bool have_context,
75 const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) {
76 // If we failed before Initialize, then do nothing.
77 if (!shared_state_)
78 return;
79
80 // Make sure that no PictureBuffer textures refer to the SurfaceTexture or to
81 // the service_id that we created for it.
82 for (const std::pair<int, media::PictureBuffer>& entry : buffers) {
83 ReleaseCodecBufferForPicture(entry.second);
84 SetImageForPicture(entry.second, nullptr);
85 }
86
87 // If we're rendering to a SurfaceTexture we can make a copy of the current
88 // front buffer so that the PictureBuffer textures are still valid.
89 if (surface_texture_ && have_context && ShouldCopyPictures())
90 CopySurfaceTextureToPictures(buffers);
91
92 // Now that no AVDACodecImages refer to the SurfaceTexture's texture, delete
93 // the texture name.
94 GLuint service_id = shared_state_->surface_texture_service_id();
95 if (service_id > 0 && have_context)
96 glDeleteTextures(1, &service_id);
97 }
98
99 scoped_refptr<gfx::SurfaceTexture>
100 AndroidDeferredRenderingBackingStrategy::GetSurfaceTexture() const {
101 return surface_texture_;
102 }
103
104 uint32_t AndroidDeferredRenderingBackingStrategy::GetTextureTarget() const {
105 // If we're using a surface texture, then we need an external texture target
106 // to sample from it. If not, then we'll use 2D transparent textures to draw
107 // a transparent hole through which to see the SurfaceView. This is normally
108 // needed only for the devtools inspector, since the overlay mechanism handles
109 // it otherwise.
110 return surface_texture_ ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
111 }
112
113 gfx::Size AndroidDeferredRenderingBackingStrategy::GetPictureBufferSize()
114 const {
115 // For SurfaceView, request a 1x1 2D texture to reduce memory during
116 // initialization. For SurfaceTexture, allocate a picture buffer that is the
117 // actual frame size. Note that it will be an external texture anyway, so it
118 // doesn't allocate an image of that size. However, it's still important to
119 // get the coded size right, so that VideoLayerImpl doesn't try to scale the
120 // texture when building the quad for it.
121 return surface_texture_ ? state_provider_->GetSize() : gfx::Size(1, 1);
122 }
123
124 void AndroidDeferredRenderingBackingStrategy::SetImageForPicture(
125 const media::PictureBuffer& picture_buffer,
126 const scoped_refptr<gpu::gles2::GLStreamTextureImage>& image) {
127 gpu::gles2::TextureRef* texture_ref =
128 state_provider_->GetTextureForPicture(picture_buffer);
129 RETURN_IF_NULL(texture_ref);
130
131 gpu::gles2::TextureManager* texture_manager =
132 state_provider_->GetGlDecoder()->GetContextGroup()->texture_manager();
133 RETURN_IF_NULL(texture_manager);
134
135 if (image) {
136 // Also set the parameters for the level if we're not clearing
137 // the image.
138 const gfx::Size size = state_provider_->GetSize();
139 texture_manager->SetLevelInfo(texture_ref, GetTextureTarget(), 0, GL_RGBA,
140 size.width(), size.height(), 1, 0, GL_RGBA,
141 GL_UNSIGNED_BYTE, gfx::Rect());
142
143 // Override the texture's service_id, so that it will use the one that
144 // will be / is attached to the SurfaceTexture.
145 DCHECK(shared_state_->surface_texture_service_id());
146 texture_ref->texture()->SetUnownedServiceId(
147 shared_state_->surface_texture_service_id());
148
149 static_cast<AVDACodecImage*>(image.get())
150 ->SetTexture(texture_ref->texture());
151 } else {
152 // Clear the unowned service_id, so that this texture is no longer going
153 // to depend on the surface texture at all.
154 texture_ref->texture()->SetUnownedServiceId(0);
155 }
156
157 // For SurfaceTexture we set the image to UNBOUND so that the implementation
158 // will call CopyTexImage, which is where AVDACodecImage updates the
159 // SurfaceTexture to the right frame.
160 // For SurfaceView we set the image to be BOUND because ScheduleOverlayPlane
161 // expects it. If something tries to sample from this texture it won't work,
162 // but there's no way to sample from a SurfaceView anyway, so it doesn't
163 // matter. The only way to use this texture is to schedule it as an overlay.
164 const gpu::gles2::Texture::ImageState image_state =
165 surface_texture_ ? gpu::gles2::Texture::UNBOUND
166 : gpu::gles2::Texture::BOUND;
167 texture_manager->SetLevelStreamTextureImage(texture_ref, GetTextureTarget(),
168 0, image.get(), image_state);
169 }
170
171 void AndroidDeferredRenderingBackingStrategy::UseCodecBufferForPictureBuffer(
172 int32_t codec_buf_index,
173 const media::PictureBuffer& picture_buffer) {
174 // Make sure that the decoder is available.
175 RETURN_IF_NULL(state_provider_->GetGlDecoder());
176
177 // Notify the AVDACodecImage for picture_buffer that it should use the
178 // decoded buffer codec_buf_index to render this frame.
179 AVDACodecImage* avda_image =
180 shared_state_->GetImageForPicture(picture_buffer.id());
181 RETURN_IF_NULL(avda_image);
182
183 // Note that this is not a race, since we do not re-use a PictureBuffer
184 // until after the CC is done drawing it.
185 pictures_out_for_display_.push_back(picture_buffer.id());
186 avda_image->SetMediaCodecBufferIndex(codec_buf_index);
187 avda_image->SetSize(state_provider_->GetSize());
188
189 MaybeRenderEarly();
190 }
191
192 void AndroidDeferredRenderingBackingStrategy::AssignOnePictureBuffer(
193 const media::PictureBuffer& picture_buffer,
194 bool have_context) {
195 // Attach a GLImage to each texture that will use the surface texture.
196 // We use a refptr here in case SetImageForPicture fails.
197 scoped_refptr<gpu::gles2::GLStreamTextureImage> gl_image =
198 new AVDACodecImage(picture_buffer.id(), shared_state_, media_codec_,
199 state_provider_->GetGlDecoder(), surface_texture_);
200 SetImageForPicture(picture_buffer, gl_image);
201
202 if (!surface_texture_ && have_context) {
203 // To make devtools work, we're using a 2D texture. Make it transparent,
204 // so that it draws a hole for the SV to show through. This is only
205 // because devtools draws and reads back, which skips overlay processing.
206 // It's unclear why devtools renders twice -- once normally, and once
207 // including a readback layer. The result is that the device screen
208 // flashes as we alternately draw the overlay hole and this texture,
209 // unless we make the texture transparent.
210 static const uint8_t rgba[] = {0, 0, 0, 0};
211 const gfx::Size size(1, 1);
212 DCHECK_LE(1u, picture_buffer.texture_ids().size());
213 glBindTexture(GL_TEXTURE_2D, picture_buffer.texture_ids()[0]);
214 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
215 GL_RGBA, GL_UNSIGNED_BYTE, rgba);
216 }
217 }
218
219 void AndroidDeferredRenderingBackingStrategy::ReleaseCodecBufferForPicture(
220 const media::PictureBuffer& picture_buffer) {
221 AVDACodecImage* avda_image =
222 shared_state_->GetImageForPicture(picture_buffer.id());
223 RETURN_IF_NULL(avda_image);
224 avda_image->UpdateSurface(AVDACodecImage::UpdateMode::DISCARD_CODEC_BUFFER);
225 }
226
227 void AndroidDeferredRenderingBackingStrategy::ReuseOnePictureBuffer(
228 const media::PictureBuffer& picture_buffer) {
229 pictures_out_for_display_.erase(
230 std::remove(pictures_out_for_display_.begin(),
231 pictures_out_for_display_.end(), picture_buffer.id()),
232 pictures_out_for_display_.end());
233
234 // At this point, the CC must be done with the picture. We can't really
235 // check for that here directly. it's guaranteed in gpu_video_decoder.cc,
236 // when it waits on the sync point before releasing the mailbox. That sync
237 // point is inserted by destroying the resource in VideoLayerImpl::DidDraw.
238 ReleaseCodecBufferForPicture(picture_buffer);
239 MaybeRenderEarly();
240 }
241
242 void AndroidDeferredRenderingBackingStrategy::ReleaseCodecBuffers(
243 const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) {
244 for (const std::pair<int, media::PictureBuffer>& entry : buffers)
245 ReleaseCodecBufferForPicture(entry.second);
246 }
247
248 void AndroidDeferredRenderingBackingStrategy::MaybeRenderEarly() {
249 // See if we can consume the front buffer / render to the SurfaceView.
250 if (pictures_out_for_display_.size() == 1u) {
251 AVDACodecImage* avda_image =
252 shared_state_->GetImageForPicture(*pictures_out_for_display_.begin());
253 RETURN_IF_NULL(avda_image);
254 avda_image->UpdateSurface(
255 AVDACodecImage::UpdateMode::RENDER_TO_FRONT_BUFFER);
256 return;
257 }
258
259 // Back buffer rendering is only available for surface textures.
260 if (!surface_texture_)
261 return;
262
263 // See if the back buffer is free. If so, then render the earliest frame. The
264 // listing is in render order, so we can just use the first unrendered frame
265 // if there is back buffer space.
266 AVDACodecImage* first_renderable_image = nullptr;
267 for (int id : pictures_out_for_display_) {
268 AVDACodecImage* avda_image = shared_state_->GetImageForPicture(id);
269 if (!avda_image)
270 continue;
271
272 // If the back buffer is unavailable, there's nothing left to do.
273 if (avda_image->is_rendered_to_back_buffer())
274 return;
275
276 // If the image is rendered to the front buffer or has been dropped, it is
277 // not valid for rendering.
278 if (avda_image->is_rendered())
279 continue;
280
281 if (!first_renderable_image)
282 first_renderable_image = avda_image;
283 }
284
285 if (first_renderable_image) {
286 first_renderable_image->UpdateSurface(
287 AVDACodecImage::UpdateMode::RENDER_TO_BACK_BUFFER);
288 }
289 }
290
291 void AndroidDeferredRenderingBackingStrategy::CodecChanged(
292 media::VideoCodecBridge* codec) {
293 media_codec_ = codec;
294 shared_state_->CodecChanged(codec);
295 }
296
297 void AndroidDeferredRenderingBackingStrategy::OnFrameAvailable() {
298 shared_state_->SignalFrameAvailable();
299 }
300
301 bool AndroidDeferredRenderingBackingStrategy::ArePicturesOverlayable() {
302 // SurfaceView frames are always overlayable because that's the only way to
303 // display them.
304 return !surface_texture_;
305 }
306
307 void AndroidDeferredRenderingBackingStrategy::UpdatePictureBufferSize(
308 media::PictureBuffer* picture_buffer,
309 const gfx::Size& new_size) {
310 // This strategy uses EGL images which manage the texture size for us. We
311 // simply update the PictureBuffer meta-data and leave the texture as-is.
312 picture_buffer->set_size(new_size);
313 }
314
315 void AndroidDeferredRenderingBackingStrategy::CopySurfaceTextureToPictures(
316 const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) {
317 DVLOG(3) << __FUNCTION__;
318
319 // Don't try to copy if the SurfaceTexture was never attached because that
320 // means it was never updated.
321 if (!shared_state_->surface_texture_is_attached())
322 return;
323
324 gpu::gles2::GLES2Decoder* gl_decoder = state_provider_->GetGlDecoder().get();
325 if (!gl_decoder)
326 return;
327
328 const gfx::Size size = state_provider_->GetSize();
329
330 // Create a 2D texture to hold a copy of the SurfaceTexture's front buffer.
331 GLuint tmp_texture_id;
332 glGenTextures(1, &tmp_texture_id);
333 {
334 gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, tmp_texture_id);
335 // The target texture's size will exactly match the source.
336 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
337 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
338 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
339 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
340 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
341 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
342 }
343
344 float transform_matrix[16];
345 surface_texture_->GetTransformMatrix(transform_matrix);
346
347 gpu::CopyTextureCHROMIUMResourceManager copier;
348 copier.Initialize(
349 gl_decoder,
350 gl_decoder->GetContextGroup()->feature_info()->feature_flags());
351 copier.DoCopyTextureWithTransform(gl_decoder, GL_TEXTURE_EXTERNAL_OES,
352 shared_state_->surface_texture_service_id(),
353 GL_TEXTURE_2D, tmp_texture_id, size.width(),
354 size.height(), true, false, false,
355 transform_matrix);
356
357 // Create an EGLImage from the 2D texture we just copied into. By associating
358 // the EGLImage with the PictureBuffer textures they will remain valid even
359 // after we delete the 2D texture and EGLImage.
360 const EGLImageKHR egl_image = eglCreateImageKHR(
361 gfx::GLSurfaceEGL::GetHardwareDisplay(), eglGetCurrentContext(),
362 EGL_GL_TEXTURE_2D_KHR, reinterpret_cast<EGLClientBuffer>(tmp_texture_id),
363 nullptr /* attrs */);
364
365 glDeleteTextures(1, &tmp_texture_id);
366 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
367
368 if (egl_image == EGL_NO_IMAGE_KHR) {
369 DLOG(ERROR) << "Failed creating EGLImage: " << ui::GetLastEGLErrorString();
370 return;
371 }
372
373 for (const std::pair<int, media::PictureBuffer>& entry : buffers) {
374 gpu::gles2::TextureRef* texture_ref =
375 state_provider_->GetTextureForPicture(entry.second);
376 if (!texture_ref)
377 continue;
378 gfx::ScopedTextureBinder texture_binder(
379 GL_TEXTURE_EXTERNAL_OES, texture_ref->texture()->service_id());
380 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_image);
381 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
382 }
383
384 EGLBoolean result =
385 eglDestroyImageKHR(gfx::GLSurfaceEGL::GetHardwareDisplay(), egl_image);
386 if (result == EGL_FALSE) {
387 DLOG(ERROR) << "Error destroying EGLImage: "
388 << ui::GetLastEGLErrorString();
389 }
390 }
391
392 bool AndroidDeferredRenderingBackingStrategy::DoesSurfaceTextureDetachWork()
393 const {
394 bool surface_texture_detach_works = true;
395 if (gpu::gles2::GLES2Decoder* gl_decoder =
396 state_provider_->GetGlDecoder().get()) {
397 if (gpu::gles2::ContextGroup* group = gl_decoder->GetContextGroup()) {
398 if (gpu::gles2::FeatureInfo* feature_info = group->feature_info()) {
399 surface_texture_detach_works =
400 !feature_info->workarounds().surface_texture_cant_detach;
401 }
402 }
403 }
404
405 // As a special case, the MicroMax A114 doesn't get the workaround, even
406 // though it should. Hardcode it here until we get a device and figure out
407 // why. crbug.com/591600
408 if (base::android::BuildInfo::GetInstance()->sdk_int() <= 18) { // JB
409 const std::string brand(
410 base::ToLowerASCII(base::android::BuildInfo::GetInstance()->brand()));
411 if (brand == "micromax") {
412 const std::string model(
413 base::ToLowerASCII(base::android::BuildInfo::GetInstance()->model()));
414 if (model.find("a114") != std::string::npos)
415 surface_texture_detach_works = false;
416 }
417 }
418
419 return surface_texture_detach_works;
420 }
421
422 bool AndroidDeferredRenderingBackingStrategy::ShouldCopyPictures() const {
423 // Mali + <= KitKat crashes when we try to do this. We don't know if it's
424 // due to detaching a surface texture, but it's the same set of devices.
425 if (!DoesSurfaceTextureDetachWork())
426 return false;
427
428 // Other devices are unreliable for other reasons (e.g., EGLImage).
429 if (gpu::gles2::GLES2Decoder* gl_decoder =
430 state_provider_->GetGlDecoder().get()) {
431 if (gpu::gles2::ContextGroup* group = gl_decoder->GetContextGroup()) {
432 if (gpu::gles2::FeatureInfo* feature_info = group->feature_info()) {
433 return !feature_info->workarounds().avda_dont_copy_pictures;
434 }
435 }
436 }
437
438 // Assume so.
439 return true;
440 }
441
442 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698