Skip to content
This repository has been archived by the owner on Apr 3, 2020. It is now read-only.

Commit

Permalink
AVDACodecImages keep their backing SurfaceTexture alive
Browse files Browse the repository at this point in the history
Previously, when destructing an AVDA, we had to delete the
AVDACodecImages attached to the PictureBuffer textures, and
use a fragile method of copying the SurfaceTexture buffer
and creating egl images to make sure those PictureBuffer
textures didn't become unbacked.

Now the AVDACodecImages are kept alive by the Textures
they're bound to, and they keep a ref to AVDASharedState
which contains the shared SurfaceTexture and its platform
texture. The AVDASharedState destructor cleans up the
SurfaceTexture and deletes the platform texture.

BUG=533630,614090
CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_optional_gpu_tests_rel;tryserver.chromium.mac:mac_optional_gpu_tests_rel;tryserver.chromium.win:win_optional_gpu_tests_rel

Review-Url: https://codereview.chromium.org/2005103004
Cr-Commit-Position: refs/heads/master@{#398293}
(cherry picked from commit 3e46593)

Review URL: https://codereview.chromium.org/2049053004 .

Cr-Commit-Position: refs/branch-heads/2743@{#290}
Cr-Branched-From: 2b3ae3b-refs/heads/master@{#394939}
  • Loading branch information
Chris Watkins committed Jun 9, 2016
1 parent 4476f79 commit 89c1f3f
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 249 deletions.
19 changes: 2 additions & 17 deletions media/gpu/android_copying_backing_strategy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,8 @@ gfx::ScopedJavaSurface AndroidCopyingBackingStrategy::Initialize(
return gfx::ScopedJavaSurface();
}

// Create a texture and attach the SurfaceTexture to it.
glGenTextures(1, &surface_texture_id_);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, surface_texture_id_);

// Note that the target will be correctly sized, so nearest filtering is all
// that's needed.
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

state_provider_->GetGlDecoder()->RestoreTextureUnitBindings(0);
state_provider_->GetGlDecoder()->RestoreActiveTexture();

surface_texture_ = gfx::SurfaceTexture::Create(surface_texture_id_);

surface_texture_ =
state_provider_->CreateAttachedSurfaceTexture(&surface_texture_id_);
return gfx::ScopedJavaSurface(surface_texture_.get());
}

Expand Down
208 changes: 30 additions & 178 deletions media/gpu/android_deferred_rendering_backing_strategy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,45 +41,24 @@ gfx::ScopedJavaSurface AndroidDeferredRenderingBackingStrategy::Initialize(
int surface_view_id) {
shared_state_ = new AVDASharedState();

// Create a texture for the SurfaceTexture to use.
GLuint service_id;
glGenTextures(1, &service_id);
shared_state_->set_surface_texture_service_id(service_id);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, service_id);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

state_provider_->GetGlDecoder()->RestoreTextureUnitBindings(0);
state_provider_->GetGlDecoder()->RestoreActiveTexture();
DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

gfx::ScopedJavaSurface surface;
bool using_virtual_context = false;
if (gfx::GLContext* context = gfx::GLContext::GetCurrent()) {
if (gfx::GLShareGroup* share_group = context->share_group())
using_virtual_context = !!share_group->GetSharedContext();
}
UMA_HISTOGRAM_BOOLEAN("Media.AVDA.VirtualContext", using_virtual_context);

// Acquire the SurfaceView surface if given a valid id.
if (surface_view_id != media::VideoDecodeAccelerator::Config::kNoSurfaceID) {
surface = gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface(
return gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface(
surface_view_id);
} else {
bool using_virtual_context = false;
if (gfx::GLContext* context = gfx::GLContext::GetCurrent()) {
if (gfx::GLShareGroup* share_group = context->share_group())
using_virtual_context = !!share_group->GetSharedContext();
}
UMA_HISTOGRAM_BOOLEAN("Media.AVDA.VirtualContext", using_virtual_context);
// Detach doesn't work so well on all platforms. Just attach the
// SurfaceTexture here, and probably context switch later.
// Detaching might save us a context switch, since AVDACodecImage will
// attach when needed. However, given that it also fails a lot, we just
// don't do it at all. If virtual contexts are in use, then it doesn't
// even save us a context switch.
surface_texture_ = gfx::SurfaceTexture::Create(service_id);
shared_state_->DidAttachSurfaceTexture();
surface = gfx::ScopedJavaSurface(surface_texture_.get());
}

return surface;
// Create a SurfaceTexture.
GLuint service_id = 0;
surface_texture_ = state_provider_->CreateAttachedSurfaceTexture(&service_id);
shared_state_->SetSurfaceTexture(surface_texture_, service_id);
return gfx::ScopedJavaSurface(surface_texture_.get());
}

void AndroidDeferredRenderingBackingStrategy::Cleanup(
Expand All @@ -89,23 +68,7 @@ void AndroidDeferredRenderingBackingStrategy::Cleanup(
if (!shared_state_)
return;

// Make sure that no PictureBuffer textures refer to the SurfaceTexture or to
// the service_id that we created for it.
for (const std::pair<int, media::PictureBuffer>& entry : buffers) {
ReleaseCodecBufferForPicture(entry.second);
SetImageForPicture(entry.second, nullptr);
}

// If we're rendering to a SurfaceTexture we can make a copy of the current
// front buffer so that the PictureBuffer textures are still valid.
if (surface_texture_ && have_context && ShouldCopyPictures())
CopySurfaceTextureToPictures(buffers);

// Now that no AVDACodecImages refer to the SurfaceTexture's texture, delete
// the texture name.
GLuint service_id = shared_state_->surface_texture_service_id();
if (service_id > 0 && have_context)
glDeleteTextures(1, &service_id);
CodecChanged(nullptr);
}

scoped_refptr<gfx::SurfaceTexture>
Expand Down Expand Up @@ -148,10 +111,11 @@ void AndroidDeferredRenderingBackingStrategy::SetImageForPicture(
// previously set.
GLuint stream_texture_service_id = 0;
if (image) {
// Override the texture's service_id, so that it will use the one that is
// attached to the SurfaceTexture.
stream_texture_service_id = shared_state_->surface_texture_service_id();
DCHECK_NE(stream_texture_service_id, 0u);
if (shared_state_->surface_texture_service_id() != 0) {
// Override the Texture's service id, so that it will use the one that is
// attached to the SurfaceTexture.
stream_texture_service_id = shared_state_->surface_texture_service_id();
}

// Also set the parameters for the level if we're not clearing the image.
const gfx::Size size = state_provider_->GetSize();
Expand All @@ -163,16 +127,17 @@ void AndroidDeferredRenderingBackingStrategy::SetImageForPicture(
->set_texture(texture_ref->texture());
}

// For SurfaceTexture we set the image to UNBOUND so that the implementation
// will call CopyTexImage, which is where AVDACodecImage updates the
// SurfaceTexture to the right frame.
// For SurfaceView we set the image to be BOUND because ScheduleOverlayPlane
// expects it. If something tries to sample from this texture it won't work,
// If we're clearing the image, or setting a SurfaceTexture backed image, we
// set the state to UNBOUND. For SurfaceTexture images, this ensures that the
// implementation will call CopyTexImage, which is where AVDACodecImage
// updates the SurfaceTexture to the right frame.
auto image_state = gpu::gles2::Texture::UNBOUND;
// For SurfaceView we set the state to BOUND because ScheduleOverlayPlane
// requires it. If something tries to sample from this texture it won't work,
// but there's no way to sample from a SurfaceView anyway, so it doesn't
// matter. The only way to use this texture is to schedule it as an overlay.
const gpu::gles2::Texture::ImageState image_state =
surface_texture_ ? gpu::gles2::Texture::UNBOUND
: gpu::gles2::Texture::BOUND;
// matter.
if (image && !surface_texture_)
image_state = gpu::gles2::Texture::BOUND;
texture_manager->SetLevelStreamTextureImage(texture_ref, GetTextureTarget(),
0, image.get(), image_state,
stream_texture_service_id);
Expand Down Expand Up @@ -334,117 +299,4 @@ void AndroidDeferredRenderingBackingStrategy::UpdatePictureBufferSize(
picture_buffer->set_size(new_size);
}

void AndroidDeferredRenderingBackingStrategy::CopySurfaceTextureToPictures(
const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) {
DVLOG(3) << __FUNCTION__;

// Don't try to copy if the SurfaceTexture was never attached because that
// means it was never updated.
if (!shared_state_->surface_texture_is_attached())
return;

gpu::gles2::GLES2Decoder* gl_decoder = state_provider_->GetGlDecoder().get();
if (!gl_decoder)
return;

const gfx::Size size = state_provider_->GetSize();

// Create a 2D texture to hold a copy of the SurfaceTexture's front buffer.
GLuint tmp_texture_id;
glGenTextures(1, &tmp_texture_id);
{
gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, tmp_texture_id);
// The target texture's size will exactly match the source.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}

float transform_matrix[16];
surface_texture_->GetTransformMatrix(transform_matrix);

gpu::CopyTextureCHROMIUMResourceManager copier;
copier.Initialize(
gl_decoder,
gl_decoder->GetContextGroup()->feature_info()->feature_flags());
copier.DoCopyTextureWithTransform(gl_decoder, GL_TEXTURE_EXTERNAL_OES,
shared_state_->surface_texture_service_id(),
GL_TEXTURE_2D, tmp_texture_id, size.width(),
size.height(), true, false, false,
transform_matrix);

// Create an EGLImage from the 2D texture we just copied into. By associating
// the EGLImage with the PictureBuffer textures they will remain valid even
// after we delete the 2D texture and EGLImage.
const EGLImageKHR egl_image = eglCreateImageKHR(
gfx::GLSurfaceEGL::GetHardwareDisplay(), eglGetCurrentContext(),
EGL_GL_TEXTURE_2D_KHR, reinterpret_cast<EGLClientBuffer>(tmp_texture_id),
nullptr /* attrs */);

glDeleteTextures(1, &tmp_texture_id);
DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

if (egl_image == EGL_NO_IMAGE_KHR) {
DLOG(ERROR) << "Failed creating EGLImage: " << ui::GetLastEGLErrorString();
return;
}

for (const std::pair<int, media::PictureBuffer>& entry : buffers) {
gpu::gles2::TextureRef* texture_ref =
state_provider_->GetTextureForPicture(entry.second);
if (!texture_ref)
continue;
gfx::ScopedTextureBinder texture_binder(
GL_TEXTURE_EXTERNAL_OES, texture_ref->texture()->service_id());
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_image);
DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
}

EGLBoolean result =
eglDestroyImageKHR(gfx::GLSurfaceEGL::GetHardwareDisplay(), egl_image);
if (result == EGL_FALSE) {
DLOG(ERROR) << "Error destroying EGLImage: " << ui::GetLastEGLErrorString();
}
}

bool AndroidDeferredRenderingBackingStrategy::ShouldCopyPictures() const {
// See if there's a workaround.
if (gpu::gles2::GLES2Decoder* gl_decoder =
state_provider_->GetGlDecoder().get()) {
if (gpu::gles2::ContextGroup* group = gl_decoder->GetContextGroup()) {
if (gpu::gles2::FeatureInfo* feature_info = group->feature_info()) {
if (feature_info->workarounds().avda_dont_copy_pictures)
return false;
}
}
}

// Samsung Galaxy Tab A, J3, and J1 Mini all like to crash on Lollipop in
// glEGLImageTargetTexture2DOES . These include SM-J105, SM-J111, SM-J120,
// SM-T280, SM-T285, and SM-J320 with various suffixes. All run lollipop and
// and have a Mali-400 gpu.
// For these devices, we must check based on the brand / model
// number, since the strings used by FeatureInfo aren't populated.
if (base::android::BuildInfo::GetInstance()->sdk_int() <= 22) { // L MR1
const std::string brand(
base::ToLowerASCII(base::android::BuildInfo::GetInstance()->brand()));
if (brand == "samsung") {
const std::string model(
base::ToLowerASCII(base::android::BuildInfo::GetInstance()->model()));
if (model.find("sm-j105") != std::string::npos ||
model.find("sm-j111") != std::string::npos ||
model.find("sm-j120") != std::string::npos ||
model.find("sm-t280") != std::string::npos ||
model.find("sm-t285") != std::string::npos ||
model.find("sm-j320") != std::string::npos)
return false;
}
}

return true;
}

} // namespace media
28 changes: 28 additions & 0 deletions media/gpu/android_video_decode_accelerator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,34 @@ gpu::gles2::TextureRef* AndroidVideoDecodeAccelerator::GetTextureForPicture(
return texture_ref;
}

scoped_refptr<gfx::SurfaceTexture>
AndroidVideoDecodeAccelerator::CreateAttachedSurfaceTexture(
GLuint* service_id) {
GLuint texture_id;
glGenTextures(1, &texture_id);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

auto gl_decoder = GetGlDecoder();
gl_decoder->RestoreTextureUnitBindings(0);
gl_decoder->RestoreActiveTexture();
DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

*service_id = texture_id;
// Previously, to reduce context switching, we used to create an unattached
// SurfaceTexture and attach it lazily in the compositor's context. But that
// was flaky because SurfaceTexture#detachFromGLContext() is buggy on a lot of
// devices. Now we attach it to the current context, which means we might have
// to context switch later to call updateTexImage(). Fortunately, if virtual
// contexts are in use, we won't have to context switch.
return gfx::SurfaceTexture::Create(texture_id);
}

void AndroidVideoDecodeAccelerator::OnDestroyingSurface(int surface_id) {
DCHECK(thread_checker_.CalledOnValidThread());
TRACE_EVENT0("media", "AVDA::OnDestroyingSurface");
Expand Down
2 changes: 2 additions & 0 deletions media/gpu/android_video_decode_accelerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ class MEDIA_GPU_EXPORT AndroidVideoDecodeAccelerator
base::WeakPtr<gpu::gles2::GLES2Decoder> GetGlDecoder() const override;
gpu::gles2::TextureRef* GetTextureForPicture(
const media::PictureBuffer& picture_buffer) override;
scoped_refptr<gfx::SurfaceTexture> CreateAttachedSurfaceTexture(
GLuint* service_id) override;
void PostError(const ::tracked_objects::Location& from_here,
media::VideoDecodeAccelerator::Error error) override;

Expand Down
11 changes: 5 additions & 6 deletions media/gpu/avda_codec_image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@

#include <memory>

#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/context_state.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "media/base/android/sdk_media_codec_bridge.h"
#include "media/gpu/avda_shared_state.h"
#include "ui/gl/android/surface_texture.h"
#include "ui/gl/gl_context.h"
Expand Down Expand Up @@ -69,10 +68,11 @@ bool AVDACodecImage::CopyTexImage(unsigned target) {

GLint bound_service_id = 0;
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
// We insist that the currently bound texture is the right one. We could
// make a new glimage from a 2D image.
if (bound_service_id != shared_state_->surface_texture_service_id())
// We insist that the currently bound texture is the right one.
if (bound_service_id !=
static_cast<GLint>(shared_state_->surface_texture_service_id())) {
return false;
}

// Make sure that we have the right image in the front buffer. Note that the
// bound_service_id is guaranteed to be equal to the surface texture's client
Expand Down Expand Up @@ -170,7 +170,6 @@ void AVDACodecImage::UpdateSurfaceInternal(
if (update_mode != UpdateMode::RENDER_TO_FRONT_BUFFER)
return;

DCHECK(shared_state_->surface_texture_is_attached());
UpdateSurfaceTexture(attached_bindings_mode);
}

Expand Down
2 changes: 2 additions & 0 deletions media/gpu/avda_codec_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class ScopedMakeCurrent;

namespace media {

class VideoCodecBridge;

// GLImage that renders MediaCodec buffers to a SurfaceTexture or SurfaceView as
// needed in order to draw them.
class AVDACodecImage : public gpu::gles2::GLStreamTextureImage {
Expand Down
Loading

0 comments on commit 89c1f3f

Please sign in to comment.