OLD | NEW |
| (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 SetImageForPicture(entry.second, nullptr); | |
84 | |
85 // If we're rendering to a SurfaceTexture we can make a copy of the current | |
86 // front buffer so that the PictureBuffer textures are still valid. | |
87 if (surface_texture_ && have_context && ShouldCopyPictures()) | |
88 CopySurfaceTextureToPictures(buffers); | |
89 | |
90 // Now that no AVDACodecImages refer to the SurfaceTexture's texture, delete | |
91 // the texture name. | |
92 GLuint service_id = shared_state_->surface_texture_service_id(); | |
93 if (service_id > 0 && have_context) | |
94 glDeleteTextures(1, &service_id); | |
95 } | |
96 | |
97 scoped_refptr<gfx::SurfaceTexture> | |
98 AndroidDeferredRenderingBackingStrategy::GetSurfaceTexture() const { | |
99 return surface_texture_; | |
100 } | |
101 | |
102 uint32_t AndroidDeferredRenderingBackingStrategy::GetTextureTarget() const { | |
103 // If we're using a surface texture, then we need an external texture target | |
104 // to sample from it. If not, then we'll use 2D transparent textures to draw | |
105 // a transparent hole through which to see the SurfaceView. This is normally | |
106 // needed only for the devtools inspector, since the overlay mechanism handles | |
107 // it otherwise. | |
108 return surface_texture_ ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; | |
109 } | |
110 | |
111 gfx::Size AndroidDeferredRenderingBackingStrategy::GetPictureBufferSize() | |
112 const { | |
113 // For SurfaceView, request a 1x1 2D texture to reduce memory during | |
114 // initialization. For SurfaceTexture, allocate a picture buffer that is the | |
115 // actual frame size. Note that it will be an external texture anyway, so it | |
116 // doesn't allocate an image of that size. However, it's still important to | |
117 // get the coded size right, so that VideoLayerImpl doesn't try to scale the | |
118 // texture when building the quad for it. | |
119 return surface_texture_ ? state_provider_->GetSize() : gfx::Size(1, 1); | |
120 } | |
121 | |
122 AVDACodecImage* AndroidDeferredRenderingBackingStrategy::GetImageForPicture( | |
123 const media::PictureBuffer& picture_buffer) { | |
124 gpu::gles2::TextureRef* texture_ref = | |
125 state_provider_->GetTextureForPicture(picture_buffer); | |
126 RETURN_NULL_IF_NULL(texture_ref); | |
127 gl::GLImage* image = | |
128 texture_ref->texture()->GetLevelImage(GetTextureTarget(), 0); | |
129 return static_cast<AVDACodecImage*>(image); | |
130 } | |
131 | |
132 void AndroidDeferredRenderingBackingStrategy::SetImageForPicture( | |
133 const media::PictureBuffer& picture_buffer, | |
134 const scoped_refptr<gpu::gles2::GLStreamTextureImage>& image) { | |
135 gpu::gles2::TextureRef* texture_ref = | |
136 state_provider_->GetTextureForPicture(picture_buffer); | |
137 RETURN_IF_NULL(texture_ref); | |
138 | |
139 gpu::gles2::TextureManager* texture_manager = | |
140 state_provider_->GetGlDecoder()->GetContextGroup()->texture_manager(); | |
141 RETURN_IF_NULL(texture_manager); | |
142 | |
143 if (image) { | |
144 // Also set the parameters for the level if we're not clearing | |
145 // the image. | |
146 const gfx::Size size = state_provider_->GetSize(); | |
147 texture_manager->SetLevelInfo(texture_ref, GetTextureTarget(), 0, GL_RGBA, | |
148 size.width(), size.height(), 1, 0, GL_RGBA, | |
149 GL_UNSIGNED_BYTE, gfx::Rect()); | |
150 | |
151 // Override the texture's service_id, so that it will use the one that | |
152 // will be / is attached to the SurfaceTexture. | |
153 DCHECK(shared_state_->surface_texture_service_id()); | |
154 texture_ref->texture()->SetUnownedServiceId( | |
155 shared_state_->surface_texture_service_id()); | |
156 | |
157 static_cast<AVDACodecImage*>(image.get()) | |
158 ->SetTexture(texture_ref->texture()); | |
159 } else { | |
160 // Clear the unowned service_id, so that this texture is no longer going | |
161 // to depend on the surface texture at all. | |
162 texture_ref->texture()->SetUnownedServiceId(0); | |
163 } | |
164 | |
165 // For SurfaceTexture we set the image to UNBOUND so that the implementation | |
166 // will call CopyTexImage, which is where AVDACodecImage updates the | |
167 // SurfaceTexture to the right frame. | |
168 // For SurfaceView we set the image to be BOUND because ScheduleOverlayPlane | |
169 // expects it. If something tries to sample from this texture it won't work, | |
170 // but there's no way to sample from a SurfaceView anyway, so it doesn't | |
171 // matter. The only way to use this texture is to schedule it as an overlay. | |
172 const gpu::gles2::Texture::ImageState image_state = | |
173 surface_texture_ ? gpu::gles2::Texture::UNBOUND | |
174 : gpu::gles2::Texture::BOUND; | |
175 texture_manager->SetLevelStreamTextureImage(texture_ref, GetTextureTarget(), | |
176 0, image.get(), image_state); | |
177 } | |
178 | |
179 void AndroidDeferredRenderingBackingStrategy::UseCodecBufferForPictureBuffer( | |
180 int32_t codec_buf_index, | |
181 const media::PictureBuffer& picture_buffer) { | |
182 // Make sure that the decoder is available. | |
183 RETURN_IF_NULL(state_provider_->GetGlDecoder()); | |
184 | |
185 // Notify the AVDACodecImage for picture_buffer that it should use the | |
186 // decoded buffer codec_buf_index to render this frame. | |
187 AVDACodecImage* avda_image = GetImageForPicture(picture_buffer); | |
188 RETURN_IF_NULL(avda_image); | |
189 DCHECK_EQ(avda_image->GetMediaCodecBufferIndex(), -1); | |
190 // Note that this is not a race, since we do not re-use a PictureBuffer | |
191 // until after the CC is done drawing it. | |
192 avda_image->SetMediaCodecBufferIndex(codec_buf_index); | |
193 avda_image->SetSize(state_provider_->GetSize()); | |
194 } | |
195 | |
196 void AndroidDeferredRenderingBackingStrategy::AssignOnePictureBuffer( | |
197 const media::PictureBuffer& picture_buffer, | |
198 bool have_context) { | |
199 // Attach a GLImage to each texture that will use the surface texture. | |
200 // We use a refptr here in case SetImageForPicture fails. | |
201 scoped_refptr<gpu::gles2::GLStreamTextureImage> gl_image = | |
202 new AVDACodecImage(shared_state_, media_codec_, | |
203 state_provider_->GetGlDecoder(), surface_texture_); | |
204 SetImageForPicture(picture_buffer, gl_image); | |
205 | |
206 if (!surface_texture_ && have_context) { | |
207 // To make devtools work, we're using a 2D texture. Make it transparent, | |
208 // so that it draws a hole for the SV to show through. This is only | |
209 // because devtools draws and reads back, which skips overlay processing. | |
210 // It's unclear why devtools renders twice -- once normally, and once | |
211 // including a readback layer. The result is that the device screen | |
212 // flashes as we alternately draw the overlay hole and this texture, | |
213 // unless we make the texture transparent. | |
214 static const uint8_t rgba[] = {0, 0, 0, 0}; | |
215 const gfx::Size size(1, 1); | |
216 DCHECK_LE(1u, picture_buffer.texture_ids().size()); | |
217 glBindTexture(GL_TEXTURE_2D, picture_buffer.texture_ids()[0]); | |
218 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, | |
219 GL_RGBA, GL_UNSIGNED_BYTE, rgba); | |
220 } | |
221 } | |
222 | |
223 void AndroidDeferredRenderingBackingStrategy::ReleaseCodecBufferForPicture( | |
224 const media::PictureBuffer& picture_buffer) { | |
225 AVDACodecImage* avda_image = GetImageForPicture(picture_buffer); | |
226 | |
227 // See if there is a media codec buffer still attached to this image. | |
228 const int32_t codec_buffer = avda_image->GetMediaCodecBufferIndex(); | |
229 | |
230 if (codec_buffer >= 0) { | |
231 // PictureBuffer wasn't displayed, so release the buffer. | |
232 media_codec_->ReleaseOutputBuffer(codec_buffer, false); | |
233 avda_image->SetMediaCodecBufferIndex(-1); | |
234 } | |
235 } | |
236 | |
237 void AndroidDeferredRenderingBackingStrategy::ReuseOnePictureBuffer( | |
238 const media::PictureBuffer& picture_buffer) { | |
239 // At this point, the CC must be done with the picture. We can't really | |
240 // check for that here directly. it's guaranteed in gpu_video_decoder.cc, | |
241 // when it waits on the sync point before releasing the mailbox. That sync | |
242 // point is inserted by destroying the resource in VideoLayerImpl::DidDraw. | |
243 ReleaseCodecBufferForPicture(picture_buffer); | |
244 } | |
245 | |
246 void AndroidDeferredRenderingBackingStrategy::CodecChanged( | |
247 media::VideoCodecBridge* codec, | |
248 const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) { | |
249 // Clear any outstanding codec buffer indices, since the new codec (if any) | |
250 // doesn't know about them. | |
251 media_codec_ = codec; | |
252 for (const std::pair<int, media::PictureBuffer>& entry : buffers) { | |
253 AVDACodecImage* avda_image = GetImageForPicture(entry.second); | |
254 avda_image->SetMediaCodec(codec); | |
255 avda_image->SetMediaCodecBufferIndex(-1); | |
256 } | |
257 } | |
258 | |
259 void AndroidDeferredRenderingBackingStrategy::OnFrameAvailable() { | |
260 shared_state_->SignalFrameAvailable(); | |
261 } | |
262 | |
263 bool AndroidDeferredRenderingBackingStrategy::ArePicturesOverlayable() { | |
264 // SurfaceView frames are always overlayable because that's the only way to | |
265 // display them. | |
266 return !surface_texture_; | |
267 } | |
268 | |
269 void AndroidDeferredRenderingBackingStrategy::UpdatePictureBufferSize( | |
270 media::PictureBuffer* picture_buffer, | |
271 const gfx::Size& new_size) { | |
272 // This strategy uses EGL images which manage the texture size for us. We | |
273 // simply update the PictureBuffer meta-data and leave the texture as-is. | |
274 picture_buffer->set_size(new_size); | |
275 } | |
276 | |
277 void AndroidDeferredRenderingBackingStrategy::CopySurfaceTextureToPictures( | |
278 const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) { | |
279 DVLOG(3) << __FUNCTION__; | |
280 | |
281 // Don't try to copy if the SurfaceTexture was never attached because that | |
282 // means it was never updated. | |
283 if (!shared_state_->surface_texture_is_attached()) | |
284 return; | |
285 | |
286 gpu::gles2::GLES2Decoder* gl_decoder = state_provider_->GetGlDecoder().get(); | |
287 if (!gl_decoder) | |
288 return; | |
289 | |
290 const gfx::Size size = state_provider_->GetSize(); | |
291 | |
292 // Create a 2D texture to hold a copy of the SurfaceTexture's front buffer. | |
293 GLuint tmp_texture_id; | |
294 glGenTextures(1, &tmp_texture_id); | |
295 { | |
296 gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, tmp_texture_id); | |
297 // The target texture's size will exactly match the source. | |
298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
299 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
302 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, | |
303 GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
304 } | |
305 | |
306 | |
307 | |
308 float transform_matrix[16]; | |
309 surface_texture_->GetTransformMatrix(transform_matrix); | |
310 | |
311 gpu::CopyTextureCHROMIUMResourceManager copier; | |
312 copier.Initialize( | |
313 gl_decoder, | |
314 gl_decoder->GetContextGroup()->feature_info()->feature_flags()); | |
315 copier.DoCopyTextureWithTransform(gl_decoder, GL_TEXTURE_EXTERNAL_OES, | |
316 shared_state_->surface_texture_service_id(), | |
317 GL_TEXTURE_2D, tmp_texture_id, size.width(), | |
318 size.height(), true, false, false, | |
319 transform_matrix); | |
320 | |
321 // Create an EGLImage from the 2D texture we just copied into. By associating | |
322 // the EGLImage with the PictureBuffer textures they will remain valid even | |
323 // after we delete the 2D texture and EGLImage. | |
324 const EGLImageKHR egl_image = eglCreateImageKHR( | |
325 gfx::GLSurfaceEGL::GetHardwareDisplay(), eglGetCurrentContext(), | |
326 EGL_GL_TEXTURE_2D_KHR, reinterpret_cast<EGLClientBuffer>(tmp_texture_id), | |
327 nullptr /* attrs */); | |
328 | |
329 glDeleteTextures(1, &tmp_texture_id); | |
330 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
331 | |
332 if (egl_image == EGL_NO_IMAGE_KHR) { | |
333 DLOG(ERROR) << "Failed creating EGLImage: " << ui::GetLastEGLErrorString(); | |
334 return; | |
335 } | |
336 | |
337 for (const std::pair<int, media::PictureBuffer>& entry : buffers) { | |
338 gpu::gles2::TextureRef* texture_ref = | |
339 state_provider_->GetTextureForPicture(entry.second); | |
340 if (!texture_ref) | |
341 continue; | |
342 gfx::ScopedTextureBinder texture_binder( | |
343 GL_TEXTURE_EXTERNAL_OES, texture_ref->texture()->service_id()); | |
344 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_image); | |
345 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
346 } | |
347 | |
348 EGLBoolean result = | |
349 eglDestroyImageKHR(gfx::GLSurfaceEGL::GetHardwareDisplay(), egl_image); | |
350 if (result == EGL_FALSE) { | |
351 DLOG(ERROR) << "Error destroying EGLImage: " | |
352 << ui::GetLastEGLErrorString(); | |
353 } | |
354 } | |
355 | |
356 bool AndroidDeferredRenderingBackingStrategy::DoesSurfaceTextureDetachWork() | |
357 const { | |
358 bool surface_texture_detach_works = true; | |
359 if (gpu::gles2::GLES2Decoder* gl_decoder = | |
360 state_provider_->GetGlDecoder().get()) { | |
361 if (gpu::gles2::ContextGroup* group = gl_decoder->GetContextGroup()) { | |
362 if (gpu::gles2::FeatureInfo* feature_info = group->feature_info()) { | |
363 surface_texture_detach_works = | |
364 !feature_info->workarounds().surface_texture_cant_detach; | |
365 } | |
366 } | |
367 } | |
368 | |
369 // As a special case, the MicroMax A114 doesn't get the workaround, even | |
370 // though it should. Hardcode it here until we get a device and figure out | |
371 // why. crbug.com/591600 | |
372 if (base::android::BuildInfo::GetInstance()->sdk_int() <= 18) { // JB | |
373 const std::string brand( | |
374 base::ToLowerASCII(base::android::BuildInfo::GetInstance()->brand())); | |
375 if (brand == "micromax") { | |
376 const std::string model( | |
377 base::ToLowerASCII(base::android::BuildInfo::GetInstance()->model())); | |
378 if (model.find("a114") != std::string::npos) | |
379 surface_texture_detach_works = false; | |
380 } | |
381 } | |
382 | |
383 return surface_texture_detach_works; | |
384 } | |
385 | |
386 bool AndroidDeferredRenderingBackingStrategy::ShouldCopyPictures() const { | |
387 // Mali + <= KitKat crashes when we try to do this. We don't know if it's | |
388 // due to detaching a surface texture, but it's the same set of devices. | |
389 if (!DoesSurfaceTextureDetachWork()) | |
390 return false; | |
391 | |
392 // Other devices are unreliable for other reasons (e.g., EGLImage). | |
393 if (gpu::gles2::GLES2Decoder* gl_decoder = | |
394 state_provider_->GetGlDecoder().get()) { | |
395 if (gpu::gles2::ContextGroup* group = gl_decoder->GetContextGroup()) { | |
396 if (gpu::gles2::FeatureInfo* feature_info = group->feature_info()) { | |
397 return !feature_info->workarounds().avda_dont_copy_pictures; | |
398 } | |
399 } | |
400 } | |
401 | |
402 // Assume so. | |
403 return true; | |
404 } | |
405 | |
406 } // namespace content | |
OLD | NEW |