| 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 bool using_virtual_context = false; |
| 45 GLuint service_id; | 45 if (gfx::GLContext* context = gfx::GLContext::GetCurrent()) { |
| 46 glGenTextures(1, &service_id); | 46 if (gfx::GLShareGroup* share_group = context->share_group()) |
| 47 shared_state_->set_surface_texture_service_id(service_id); | 47 using_virtual_context = !!share_group->GetSharedContext(); |
| 48 } |
| 49 UMA_HISTOGRAM_BOOLEAN("Media.AVDA.VirtualContext", using_virtual_context); |
| 48 | 50 |
| 49 glActiveTexture(GL_TEXTURE0); | 51 // Acquire the SurfaceView surface if given a valid id. |
| 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) { | 52 if (surface_view_id != media::VideoDecodeAccelerator::Config::kNoSurfaceID) { |
| 62 surface = gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface( | 53 return gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface( |
| 63 surface_view_id); | 54 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 } | 55 } |
| 81 | 56 |
| 82 return surface; | 57 // Create a SurfaceTexture. |
| 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 22 matching lines...) Expand all Loading... |
| 141 RETURN_IF_NULL(texture_ref); | 104 RETURN_IF_NULL(texture_ref); |
| 142 | 105 |
| 143 gpu::gles2::TextureManager* texture_manager = | 106 gpu::gles2::TextureManager* texture_manager = |
| 144 state_provider_->GetGlDecoder()->GetContextGroup()->texture_manager(); | 107 state_provider_->GetGlDecoder()->GetContextGroup()->texture_manager(); |
| 145 RETURN_IF_NULL(texture_manager); | 108 RETURN_IF_NULL(texture_manager); |
| 146 | 109 |
| 147 // Default to zero which will clear the stream texture service id if one was | 110 // Default to zero which will clear the stream texture service id if one was |
| 148 // previously set. | 111 // previously set. |
| 149 GLuint stream_texture_service_id = 0; | 112 GLuint stream_texture_service_id = 0; |
| 150 if (image) { | 113 if (image) { |
| 151 // Override the texture's service_id, so that it will use the one that is | 114 if (shared_state_->surface_texture_service_id() != 0) { |
| 152 // attached to the SurfaceTexture. | 115 // Override the Texture's service id, so that it will use the one that is |
| 153 stream_texture_service_id = shared_state_->surface_texture_service_id(); | 116 // attached to the SurfaceTexture. |
| 154 DCHECK_NE(stream_texture_service_id, 0u); | 117 stream_texture_service_id = shared_state_->surface_texture_service_id(); |
| 118 } |
| 155 | 119 |
| 156 // Also set the parameters for the level if we're not clearing the image. | 120 // Also set the parameters for the level if we're not clearing the image. |
| 157 const gfx::Size size = state_provider_->GetSize(); | 121 const gfx::Size size = state_provider_->GetSize(); |
| 158 texture_manager->SetLevelInfo(texture_ref, GetTextureTarget(), 0, GL_RGBA, | 122 texture_manager->SetLevelInfo(texture_ref, GetTextureTarget(), 0, GL_RGBA, |
| 159 size.width(), size.height(), 1, 0, GL_RGBA, | 123 size.width(), size.height(), 1, 0, GL_RGBA, |
| 160 GL_UNSIGNED_BYTE, gfx::Rect()); | 124 GL_UNSIGNED_BYTE, gfx::Rect()); |
| 161 | 125 |
| 162 static_cast<AVDACodecImage*>(image.get()) | 126 static_cast<AVDACodecImage*>(image.get()) |
| 163 ->set_texture(texture_ref->texture()); | 127 ->set_texture(texture_ref->texture()); |
| 164 } | 128 } |
| 165 | 129 |
| 166 // For SurfaceTexture we set the image to UNBOUND so that the implementation | 130 // If we're clearing the image, or setting a SurfaceTexture backed image, we |
| 167 // will call CopyTexImage, which is where AVDACodecImage updates the | 131 // set the state to UNBOUND. For SurfaceTexture images, this ensures that the |
| 168 // SurfaceTexture to the right frame. | 132 // implementation will call CopyTexImage, which is where AVDACodecImage |
| 169 // For SurfaceView we set the image to be BOUND because ScheduleOverlayPlane | 133 // updates the SurfaceTexture to the right frame. |
| 170 // expects it. If something tries to sample from this texture it won't work, | 134 auto image_state = gpu::gles2::Texture::UNBOUND; |
| 135 // For SurfaceView we set the state to BOUND because ScheduleOverlayPlane |
| 136 // requires it. If something tries to sample from this texture it won't work, |
| 171 // but there's no way to sample from a SurfaceView anyway, so it doesn't | 137 // but there's no way to sample from a SurfaceView anyway, so it doesn't |
| 172 // matter. The only way to use this texture is to schedule it as an overlay. | 138 // matter. |
| 173 const gpu::gles2::Texture::ImageState image_state = | 139 if (image && !surface_texture_) |
| 174 surface_texture_ ? gpu::gles2::Texture::UNBOUND | 140 image_state = gpu::gles2::Texture::BOUND; |
| 175 : gpu::gles2::Texture::BOUND; | |
| 176 texture_manager->SetLevelStreamTextureImage(texture_ref, GetTextureTarget(), | 141 texture_manager->SetLevelStreamTextureImage(texture_ref, GetTextureTarget(), |
| 177 0, image.get(), image_state, | 142 0, image.get(), image_state, |
| 178 stream_texture_service_id); | 143 stream_texture_service_id); |
| 179 } | 144 } |
| 180 | 145 |
| 181 void AndroidDeferredRenderingBackingStrategy::UseCodecBufferForPictureBuffer( | 146 void AndroidDeferredRenderingBackingStrategy::UseCodecBufferForPictureBuffer( |
| 182 int32_t codec_buf_index, | 147 int32_t codec_buf_index, |
| 183 const media::PictureBuffer& picture_buffer) { | 148 const media::PictureBuffer& picture_buffer) { |
| 184 // Make sure that the decoder is available. | 149 // Make sure that the decoder is available. |
| 185 RETURN_IF_NULL(state_provider_->GetGlDecoder()); | 150 RETURN_IF_NULL(state_provider_->GetGlDecoder()); |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 327 } | 292 } |
| 328 | 293 |
| 329 void AndroidDeferredRenderingBackingStrategy::UpdatePictureBufferSize( | 294 void AndroidDeferredRenderingBackingStrategy::UpdatePictureBufferSize( |
| 330 media::PictureBuffer* picture_buffer, | 295 media::PictureBuffer* picture_buffer, |
| 331 const gfx::Size& new_size) { | 296 const gfx::Size& new_size) { |
| 332 // This strategy uses EGL images which manage the texture size for us. We | 297 // This strategy uses EGL images which manage the texture size for us. We |
| 333 // simply update the PictureBuffer meta-data and leave the texture as-is. | 298 // simply update the PictureBuffer meta-data and leave the texture as-is. |
| 334 picture_buffer->set_size(new_size); | 299 picture_buffer->set_size(new_size); |
| 335 } | 300 } |
| 336 | 301 |
| 337 void AndroidDeferredRenderingBackingStrategy::CopySurfaceTextureToPictures( | |
| 338 const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) { | |
| 339 DVLOG(3) << __FUNCTION__; | |
| 340 | |
| 341 // Don't try to copy if the SurfaceTexture was never attached because that | |
| 342 // means it was never updated. | |
| 343 if (!shared_state_->surface_texture_is_attached()) | |
| 344 return; | |
| 345 | |
| 346 gpu::gles2::GLES2Decoder* gl_decoder = state_provider_->GetGlDecoder().get(); | |
| 347 if (!gl_decoder) | |
| 348 return; | |
| 349 | |
| 350 const gfx::Size size = state_provider_->GetSize(); | |
| 351 | |
| 352 // Create a 2D texture to hold a copy of the SurfaceTexture's front buffer. | |
| 353 GLuint tmp_texture_id; | |
| 354 glGenTextures(1, &tmp_texture_id); | |
| 355 { | |
| 356 gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, tmp_texture_id); | |
| 357 // The target texture's size will exactly match the source. | |
| 358 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| 359 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| 360 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| 361 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| 362 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, | |
| 363 GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
| 364 } | |
| 365 | |
| 366 float transform_matrix[16]; | |
| 367 surface_texture_->GetTransformMatrix(transform_matrix); | |
| 368 | |
| 369 gpu::CopyTextureCHROMIUMResourceManager copier; | |
| 370 copier.Initialize( | |
| 371 gl_decoder, | |
| 372 gl_decoder->GetContextGroup()->feature_info()->feature_flags()); | |
| 373 copier.DoCopyTextureWithTransform(gl_decoder, GL_TEXTURE_EXTERNAL_OES, | |
| 374 shared_state_->surface_texture_service_id(), | |
| 375 GL_TEXTURE_2D, tmp_texture_id, size.width(), | |
| 376 size.height(), true, false, false, | |
| 377 transform_matrix); | |
| 378 | |
| 379 // Create an EGLImage from the 2D texture we just copied into. By associating | |
| 380 // the EGLImage with the PictureBuffer textures they will remain valid even | |
| 381 // after we delete the 2D texture and EGLImage. | |
| 382 const EGLImageKHR egl_image = eglCreateImageKHR( | |
| 383 gfx::GLSurfaceEGL::GetHardwareDisplay(), eglGetCurrentContext(), | |
| 384 EGL_GL_TEXTURE_2D_KHR, reinterpret_cast<EGLClientBuffer>(tmp_texture_id), | |
| 385 nullptr /* attrs */); | |
| 386 | |
| 387 glDeleteTextures(1, &tmp_texture_id); | |
| 388 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 389 | |
| 390 if (egl_image == EGL_NO_IMAGE_KHR) { | |
| 391 DLOG(ERROR) << "Failed creating EGLImage: " << ui::GetLastEGLErrorString(); | |
| 392 return; | |
| 393 } | |
| 394 | |
| 395 for (const std::pair<int, media::PictureBuffer>& entry : buffers) { | |
| 396 gpu::gles2::TextureRef* texture_ref = | |
| 397 state_provider_->GetTextureForPicture(entry.second); | |
| 398 if (!texture_ref) | |
| 399 continue; | |
| 400 gfx::ScopedTextureBinder texture_binder( | |
| 401 GL_TEXTURE_EXTERNAL_OES, texture_ref->texture()->service_id()); | |
| 402 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_image); | |
| 403 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | |
| 404 } | |
| 405 | |
| 406 EGLBoolean result = | |
| 407 eglDestroyImageKHR(gfx::GLSurfaceEGL::GetHardwareDisplay(), egl_image); | |
| 408 if (result == EGL_FALSE) { | |
| 409 DLOG(ERROR) << "Error destroying EGLImage: " << ui::GetLastEGLErrorString(); | |
| 410 } | |
| 411 } | |
| 412 | |
| 413 bool AndroidDeferredRenderingBackingStrategy::ShouldCopyPictures() const { | |
| 414 // See if there's a workaround. | |
| 415 if (gpu::gles2::GLES2Decoder* gl_decoder = | |
| 416 state_provider_->GetGlDecoder().get()) { | |
| 417 if (gpu::gles2::ContextGroup* group = gl_decoder->GetContextGroup()) { | |
| 418 if (gpu::gles2::FeatureInfo* feature_info = group->feature_info()) { | |
| 419 if (feature_info->workarounds().avda_dont_copy_pictures) | |
| 420 return false; | |
| 421 } | |
| 422 } | |
| 423 } | |
| 424 | |
| 425 // Samsung Galaxy Tab A, J3, and J1 Mini all like to crash on Lollipop in | |
| 426 // glEGLImageTargetTexture2DOES . These include SM-J105, SM-J111, SM-J120, | |
| 427 // SM-T280, SM-T285, and SM-J320 with various suffixes. All run lollipop and | |
| 428 // and have a Mali-400 gpu. | |
| 429 // For these devices, we must check based on the brand / model | |
| 430 // number, since the strings used by FeatureInfo aren't populated. | |
| 431 if (base::android::BuildInfo::GetInstance()->sdk_int() <= 22) { // L MR1 | |
| 432 const std::string brand( | |
| 433 base::ToLowerASCII(base::android::BuildInfo::GetInstance()->brand())); | |
| 434 if (brand == "samsung") { | |
| 435 const std::string model( | |
| 436 base::ToLowerASCII(base::android::BuildInfo::GetInstance()->model())); | |
| 437 if (model.find("sm-j105") != std::string::npos || | |
| 438 model.find("sm-j111") != std::string::npos || | |
| 439 model.find("sm-j120") != std::string::npos || | |
| 440 model.find("sm-t280") != std::string::npos || | |
| 441 model.find("sm-t285") != std::string::npos || | |
| 442 model.find("sm-j320") != std::string::npos) | |
| 443 return false; | |
| 444 } | |
| 445 } | |
| 446 | |
| 447 return true; | |
| 448 } | |
| 449 | |
| 450 } // namespace media | 302 } // namespace media |
| OLD | NEW |