OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 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 "media/gpu/android_deferred_rendering_backing_strategy.h" | 5 #include "media/gpu/android_deferred_rendering_backing_strategy.h" |
6 | 6 |
7 #include <EGL/egl.h> | 7 #include <EGL/egl.h> |
8 #include <EGL/eglext.h> | 8 #include <EGL/eglext.h> |
9 | 9 |
10 #include "base/android/build_info.h" | 10 #include "base/android/build_info.h" |
(...skipping 23 matching lines...) Expand all Loading... |
34 AndroidDeferredRenderingBackingStrategy(AVDAStateProvider* state_provider) | 34 AndroidDeferredRenderingBackingStrategy(AVDAStateProvider* state_provider) |
35 : state_provider_(state_provider), media_codec_(nullptr) {} | 35 : state_provider_(state_provider), media_codec_(nullptr) {} |
36 | 36 |
37 AndroidDeferredRenderingBackingStrategy:: | 37 AndroidDeferredRenderingBackingStrategy:: |
38 ~AndroidDeferredRenderingBackingStrategy() {} | 38 ~AndroidDeferredRenderingBackingStrategy() {} |
39 | 39 |
40 gfx::ScopedJavaSurface AndroidDeferredRenderingBackingStrategy::Initialize( | 40 gfx::ScopedJavaSurface AndroidDeferredRenderingBackingStrategy::Initialize( |
41 int surface_view_id) { | 41 int surface_view_id) { |
42 shared_state_ = new AVDASharedState(); | 42 shared_state_ = new AVDASharedState(); |
43 | 43 |
44 // Create a texture for the SurfaceTexture to use. | 44 // Acquire the SurfaceView surface if given a valid id. |
45 GLuint service_id; | |
46 glGenTextures(1, &service_id); | |
47 shared_state_->set_surface_texture_service_id(service_id); | |
48 | |
49 glActiveTexture(GL_TEXTURE0); | |
50 glBindTexture(GL_TEXTURE_EXTERNAL_OES, service_id); | |
51 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
52 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
53 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
54 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
55 | |
56 state_provider_->GetGlDecoder()->RestoreTextureUnitBindings(0); | |
57 state_provider_->GetGlDecoder()->RestoreActiveTexture(); | |
58 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
59 | |
60 gfx::ScopedJavaSurface surface; | |
61 if (surface_view_id != media::VideoDecodeAccelerator::Config::kNoSurfaceID) { | 45 if (surface_view_id != media::VideoDecodeAccelerator::Config::kNoSurfaceID) { |
62 surface = gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface( | 46 return gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface( |
63 surface_view_id); | 47 surface_view_id); |
64 } else { | |
65 bool using_virtual_context = false; | |
66 if (gfx::GLContext* context = gfx::GLContext::GetCurrent()) { | |
67 if (gfx::GLShareGroup* share_group = context->share_group()) | |
68 using_virtual_context = !!share_group->GetSharedContext(); | |
69 } | |
70 UMA_HISTOGRAM_BOOLEAN("Media.AVDA.VirtualContext", using_virtual_context); | |
71 // Detach doesn't work so well on all platforms. Just attach the | |
72 // SurfaceTexture here, and probably context switch later. | |
73 // Detaching might save us a context switch, since AVDACodecImage will | |
74 // attach when needed. However, given that it also fails a lot, we just | |
75 // don't do it at all. If virtual contexts are in use, then it doesn't | |
76 // even save us a context switch. | |
77 surface_texture_ = gfx::SurfaceTexture::Create(service_id); | |
78 shared_state_->DidAttachSurfaceTexture(); | |
79 surface = gfx::ScopedJavaSurface(surface_texture_.get()); | |
80 } | 48 } |
81 | 49 |
82 return surface; | 50 // Create a SurfaceTexture. |
| 51 bool using_virtual_context = false; |
| 52 if (gfx::GLContext* context = gfx::GLContext::GetCurrent()) { |
| 53 if (gfx::GLShareGroup* share_group = context->share_group()) |
| 54 using_virtual_context = !!share_group->GetSharedContext(); |
| 55 } |
| 56 UMA_HISTOGRAM_BOOLEAN("Media.AVDA.VirtualContext", using_virtual_context); |
| 57 |
| 58 GLuint service_id = 0; |
| 59 surface_texture_ = state_provider_->CreateAttachedSurfaceTexture(&service_id); |
| 60 shared_state_->SetSurfaceTexture(surface_texture_, service_id); |
| 61 return gfx::ScopedJavaSurface(surface_texture_.get()); |
83 } | 62 } |
84 | 63 |
85 void AndroidDeferredRenderingBackingStrategy::Cleanup( | 64 void AndroidDeferredRenderingBackingStrategy::Cleanup( |
86 bool have_context, | 65 bool have_context, |
87 const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) { | 66 const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) { |
88 // If we failed before Initialize, then do nothing. | 67 // If we failed before Initialize, then do nothing. |
89 if (!shared_state_) | 68 if (!shared_state_) |
90 return; | 69 return; |
91 | 70 |
92 // Make sure that no PictureBuffer textures refer to the SurfaceTexture or to | 71 CodecChanged(nullptr); |
93 // the service_id that we created for it. | |
94 for (const std::pair<int, media::PictureBuffer>& entry : buffers) { | |
95 ReleaseCodecBufferForPicture(entry.second); | |
96 SetImageForPicture(entry.second, nullptr); | |
97 } | |
98 | |
99 // If we're rendering to a SurfaceTexture we can make a copy of the current | |
100 // front buffer so that the PictureBuffer textures are still valid. | |
101 if (surface_texture_ && have_context && ShouldCopyPictures()) | |
102 CopySurfaceTextureToPictures(buffers); | |
103 | |
104 // Now that no AVDACodecImages refer to the SurfaceTexture's texture, delete | |
105 // the texture name. | |
106 GLuint service_id = shared_state_->surface_texture_service_id(); | |
107 if (service_id > 0 && have_context) | |
108 glDeleteTextures(1, &service_id); | |
109 } | 72 } |
110 | 73 |
111 scoped_refptr<gfx::SurfaceTexture> | 74 scoped_refptr<gfx::SurfaceTexture> |
112 AndroidDeferredRenderingBackingStrategy::GetSurfaceTexture() const { | 75 AndroidDeferredRenderingBackingStrategy::GetSurfaceTexture() const { |
113 return surface_texture_; | 76 return surface_texture_; |
114 } | 77 } |
115 | 78 |
116 uint32_t AndroidDeferredRenderingBackingStrategy::GetTextureTarget() const { | 79 uint32_t AndroidDeferredRenderingBackingStrategy::GetTextureTarget() const { |
117 // If we're using a surface texture, then we need an external texture target | 80 // If we're using a surface texture, then we need an external texture target |
118 // to sample from it. If not, then we'll use 2D transparent textures to draw | 81 // to sample from it. If not, then we'll use 2D transparent textures to draw |
(...skipping 27 matching lines...) Expand all Loading... |
146 | 109 |
147 if (image) { | 110 if (image) { |
148 // Also set the parameters for the level if we're not clearing | 111 // Also set the parameters for the level if we're not clearing |
149 // the image. | 112 // the image. |
150 const gfx::Size size = state_provider_->GetSize(); | 113 const gfx::Size size = state_provider_->GetSize(); |
151 texture_manager->SetLevelInfo(texture_ref, GetTextureTarget(), 0, GL_RGBA, | 114 texture_manager->SetLevelInfo(texture_ref, GetTextureTarget(), 0, GL_RGBA, |
152 size.width(), size.height(), 1, 0, GL_RGBA, | 115 size.width(), size.height(), 1, 0, GL_RGBA, |
153 GL_UNSIGNED_BYTE, gfx::Rect()); | 116 GL_UNSIGNED_BYTE, gfx::Rect()); |
154 | 117 |
155 // Override the texture's service_id, so that it will use the one that | 118 // Override the texture's service_id, so that it will use the one that |
156 // will be / is attached to the SurfaceTexture. | 119 // is attached to the SurfaceTexture. |
157 DCHECK(shared_state_->surface_texture_service_id()); | 120 if (shared_state_->surface_texture_service_id()) { |
158 texture_ref->texture()->SetUnownedServiceId( | 121 texture_ref->texture()->SetUnownedServiceId( |
159 shared_state_->surface_texture_service_id()); | 122 shared_state_->surface_texture_service_id()); |
| 123 } |
160 | 124 |
161 static_cast<AVDACodecImage*>(image.get()) | 125 static_cast<AVDACodecImage*>(image.get()) |
162 ->set_texture(texture_ref->texture()); | 126 ->set_texture(texture_ref->texture()); |
163 } else { | 127 } else { |
164 // Clear the unowned service_id, so that this texture is no longer going | 128 // Clear the unowned service_id, so that this texture is no longer going |
165 // to depend on the surface texture at all. | 129 // to depend on the surface texture at all. |
166 texture_ref->texture()->SetUnownedServiceId(0); | 130 texture_ref->texture()->SetUnownedServiceId(0); |
167 } | 131 } |
168 | 132 |
169 // For SurfaceTexture we set the image to UNBOUND so that the implementation | 133 // For SurfaceTexture we set the image to UNBOUND so that the implementation |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
329 } | 293 } |
330 | 294 |
331 void AndroidDeferredRenderingBackingStrategy::UpdatePictureBufferSize( | 295 void AndroidDeferredRenderingBackingStrategy::UpdatePictureBufferSize( |
332 media::PictureBuffer* picture_buffer, | 296 media::PictureBuffer* picture_buffer, |
333 const gfx::Size& new_size) { | 297 const gfx::Size& new_size) { |
334 // This strategy uses EGL images which manage the texture size for us. We | 298 // This strategy uses EGL images which manage the texture size for us. We |
335 // simply update the PictureBuffer meta-data and leave the texture as-is. | 299 // simply update the PictureBuffer meta-data and leave the texture as-is. |
336 picture_buffer->set_size(new_size); | 300 picture_buffer->set_size(new_size); |
337 } | 301 } |
338 | 302 |
339 void AndroidDeferredRenderingBackingStrategy::CopySurfaceTextureToPictures( | |
340 const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) { | |
341 DVLOG(3) << __FUNCTION__; | |
342 | |
343 // Don't try to copy if the SurfaceTexture was never attached because that | |
344 // means it was never updated. | |
345 if (!shared_state_->surface_texture_is_attached()) | |
346 return; | |
347 | |
348 gpu::gles2::GLES2Decoder* gl_decoder = state_provider_->GetGlDecoder().get(); | |
349 if (!gl_decoder) | |
350 return; | |
351 | |
352 const gfx::Size size = state_provider_->GetSize(); | |
353 | |
354 // Create a 2D texture to hold a copy of the SurfaceTexture's front buffer. | |
355 GLuint tmp_texture_id; | |
356 glGenTextures(1, &tmp_texture_id); | |
357 { | |
358 gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, tmp_texture_id); | |
359 // The target texture's size will exactly match the source. | |
360 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
361 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
362 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
363 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
364 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, | |
365 GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
366 } | |
367 | |
368 float transform_matrix[16]; | |
369 surface_texture_->GetTransformMatrix(transform_matrix); | |
370 | |
371 gpu::CopyTextureCHROMIUMResourceManager copier; | |
372 copier.Initialize( | |
373 gl_decoder, | |
374 gl_decoder->GetContextGroup()->feature_info()->feature_flags()); | |
375 copier.DoCopyTextureWithTransform(gl_decoder, GL_TEXTURE_EXTERNAL_OES, | |
376 shared_state_->surface_texture_service_id(), | |
377 GL_TEXTURE_2D, tmp_texture_id, size.width(), | |
378 size.height(), true, false, false, | |
379 transform_matrix); | |
380 | |
381 // Create an EGLImage from the 2D texture we just copied into. By associating | |
382 // the EGLImage with the PictureBuffer textures they will remain valid even | |
383 // after we delete the 2D texture and EGLImage. | |
384 const EGLImageKHR egl_image = eglCreateImageKHR( | |
385 gfx::GLSurfaceEGL::GetHardwareDisplay(), eglGetCurrentContext(), | |
386 EGL_GL_TEXTURE_2D_KHR, reinterpret_cast<EGLClientBuffer>(tmp_texture_id), | |
387 nullptr /* attrs */); | |
388 | |
389 glDeleteTextures(1, &tmp_texture_id); | |
390 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
391 | |
392 if (egl_image == EGL_NO_IMAGE_KHR) { | |
393 DLOG(ERROR) << "Failed creating EGLImage: " << ui::GetLastEGLErrorString(); | |
394 return; | |
395 } | |
396 | |
397 for (const std::pair<int, media::PictureBuffer>& entry : buffers) { | |
398 gpu::gles2::TextureRef* texture_ref = | |
399 state_provider_->GetTextureForPicture(entry.second); | |
400 if (!texture_ref) | |
401 continue; | |
402 gfx::ScopedTextureBinder texture_binder( | |
403 GL_TEXTURE_EXTERNAL_OES, texture_ref->texture()->service_id()); | |
404 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_image); | |
405 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
406 } | |
407 | |
408 EGLBoolean result = | |
409 eglDestroyImageKHR(gfx::GLSurfaceEGL::GetHardwareDisplay(), egl_image); | |
410 if (result == EGL_FALSE) { | |
411 DLOG(ERROR) << "Error destroying EGLImage: " << ui::GetLastEGLErrorString(); | |
412 } | |
413 } | |
414 | |
415 bool AndroidDeferredRenderingBackingStrategy::ShouldCopyPictures() const { | |
416 // See if there's a workaround. | |
417 if (gpu::gles2::GLES2Decoder* gl_decoder = | |
418 state_provider_->GetGlDecoder().get()) { | |
419 if (gpu::gles2::ContextGroup* group = gl_decoder->GetContextGroup()) { | |
420 if (gpu::gles2::FeatureInfo* feature_info = group->feature_info()) { | |
421 if (feature_info->workarounds().avda_dont_copy_pictures) | |
422 return false; | |
423 } | |
424 } | |
425 } | |
426 | |
427 // Samsung Galaxy Tab A, J3, and J1 Mini all like to crash on Lollipop in | |
428 // glEGLImageTargetTexture2DOES . These include SM-J105, SM-J111, SM-J120, | |
429 // SM-T280, SM-T285, and SM-J320 with various suffixes. All run lollipop and | |
430 // and have a Mali-400 gpu. | |
431 // For these devices, we must check based on the brand / model | |
432 // number, since the strings used by FeatureInfo aren't populated. | |
433 if (base::android::BuildInfo::GetInstance()->sdk_int() <= 22) { // L MR1 | |
434 const std::string brand( | |
435 base::ToLowerASCII(base::android::BuildInfo::GetInstance()->brand())); | |
436 if (brand == "samsung") { | |
437 const std::string model( | |
438 base::ToLowerASCII(base::android::BuildInfo::GetInstance()->model())); | |
439 if (model.find("sm-j105") != std::string::npos || | |
440 model.find("sm-j111") != std::string::npos || | |
441 model.find("sm-j120") != std::string::npos || | |
442 model.find("sm-t280") != std::string::npos || | |
443 model.find("sm-t285") != std::string::npos || | |
444 model.find("sm-j320") != std::string::npos) | |
445 return false; | |
446 } | |
447 } | |
448 | |
449 return true; | |
450 } | |
451 | |
452 } // namespace media | 303 } // namespace media |
OLD | NEW |