Skip to content

Commit

Permalink
Added GLStreamTextureImage : GLImage, which exposes
Browse files Browse the repository at this point in the history
GetTextureMatrix.  This provides a texture matrix to
convert from hardware-specific texture coordinates.  Made
AVDACodecImage provide the current matrix from the SurfaceTexture.

Updates GLRenderer::DrawStreamVideoQuad to use the current texture
matrix from the GLImage if available, else fall back to the one
recorded quad.  Once stream_texture_android.cc returns the most
recent texture matrix, it may be possible to remove the entire
DidUpdateMatrix path in VideoFrameProvider.

Does not address CopyTextureCHROMIUM.

BUG=530681,226218
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

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

Cr-Commit-Position: refs/heads/master@{#377150}
  • Loading branch information
liberato-at-chromium authored and Commit bot committed Feb 24, 2016
1 parent e8547b1 commit df64f2c
Show file tree
Hide file tree
Showing 32 changed files with 981 additions and 413 deletions.
13 changes: 11 additions & 2 deletions cc/output/gl_renderer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2192,14 +2192,23 @@ void GLRenderer::DrawStreamVideoQuad(const DrawingFrame* frame,
SetUseProgram(program->program());

ToGLMatrix(&gl_matrix[0], quad->matrix);
gl_->UniformMatrix4fv(program->vertex_shader().tex_matrix_location(), 1,
false, gl_matrix);

ResourceProvider::ScopedReadLockGL lock(resource_provider_,
quad->resource_id());

DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_));
gl_->BindTexture(GL_TEXTURE_EXTERNAL_OES, lock.texture_id());

// TODO(liberato): stream_texture_android should stop sending |gl_matrix| to
// the video frame provider with this change (and to us), but it should
// start reporting the current matrix via
// GLStreamTextureImage::GetTextureMatrix. Until then, though, this will use
// the matrix that we provide to it, unless the GLStreamTextureImage
// overrides it. This lets it also work with AVDACodecImage, which provides
// the correct custom matrix and supplies a default one to us.
gl_->UniformMatrix4fvStreamTextureMatrixCHROMIUM(
program->vertex_shader().tex_matrix_location(), false, gl_matrix);

gl_->Uniform1i(program->fragment_shader().sampler_location(), 0);

SetShaderOpacity(quad->shared_quad_state->opacity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "content/common/gpu/media/avda_codec_image.h"
#include "content/common/gpu/media/avda_return_on_failure.h"
#include "content/common/gpu/media/avda_shared_state.h"
#include "gpu/command_buffer/service/gl_stream_texture_image.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "ui/gl/android/surface_texture.h"
#include "ui/gl/gl_bindings.h"
Expand Down Expand Up @@ -105,7 +106,7 @@ AVDACodecImage* AndroidDeferredRenderingBackingStrategy::GetImageForPicture(

void AndroidDeferredRenderingBackingStrategy::SetImageForPicture(
const media::PictureBuffer& picture_buffer,
const scoped_refptr<gl::GLImage>& image) {
const scoped_refptr<gpu::gles2::GLStreamTextureImage>& image) {
gpu::gles2::TextureRef* texture_ref = GetTextureForPicture(picture_buffer);
RETURN_IF_NULL(texture_ref);

Expand Down Expand Up @@ -145,8 +146,8 @@ void AndroidDeferredRenderingBackingStrategy::SetImageForPicture(
const gpu::gles2::Texture::ImageState image_state =
surface_texture_ ? gpu::gles2::Texture::UNBOUND
: gpu::gles2::Texture::BOUND;
texture_manager->SetLevelImage(texture_ref, GetTextureTarget(), 0,
image.get(), image_state);
texture_manager->SetLevelStreamTextureImage(texture_ref, GetTextureTarget(),
0, image.get(), image_state);
}

void AndroidDeferredRenderingBackingStrategy::UseCodecBufferForPictureBuffer(
Expand All @@ -170,7 +171,7 @@ void AndroidDeferredRenderingBackingStrategy::AssignOnePictureBuffer(
const media::PictureBuffer& picture_buffer) {
// Attach a GLImage to each texture that will use the surface texture.
// We use a refptr here in case SetImageForPicture fails.
scoped_refptr<gl::GLImage> gl_image =
scoped_refptr<gpu::gles2::GLStreamTextureImage> gl_image =
new AVDACodecImage(shared_state_, media_codec_,
state_provider_->GetGlDecoder(), surface_texture_);
SetImageForPicture(picture_buffer, gl_image);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class GLImage;

namespace gpu {
namespace gles2 {
class GLStreamTextureImage;
class TextureRef;
}
}
Expand Down Expand Up @@ -64,8 +65,9 @@ class CONTENT_EXPORT AndroidDeferredRenderingBackingStrategy

// Return the AVDACodecImage for a given PictureBuffer's texture.
AVDACodecImage* GetImageForPicture(const media::PictureBuffer&);
void SetImageForPicture(const media::PictureBuffer& picture_buffer,
const scoped_refptr<gl::GLImage>& image);
void SetImageForPicture(
const media::PictureBuffer& picture_buffer,
const scoped_refptr<gpu::gles2::GLStreamTextureImage>& image);

scoped_refptr<AVDASharedState> shared_state_;

Expand Down
122 changes: 69 additions & 53 deletions content/common/gpu/media/avda_codec_image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ AVDACodecImage::AVDACodecImage(
decoder_(decoder),
surface_texture_(surface_texture),
detach_surface_texture_on_destruction_(false),
texture_(0),
need_shader_info_(true),
texmatrix_uniform_location_(-1) {
texture_(0) {
// Default to a sane guess of "flip Y", just in case we can't get
// the matrix on the first call.
memset(gl_matrix_, 0, sizeof(gl_matrix_));
gl_matrix_[0] = gl_matrix_[5] = gl_matrix_[10] = gl_matrix_[15] = 1.0f;
gl_matrix_[0] = gl_matrix_[10] = gl_matrix_[15] = 1.0f;
gl_matrix_[5] = -1.0f;
}

AVDACodecImage::~AVDACodecImage() {}
Expand Down Expand Up @@ -61,35 +62,29 @@ bool AVDACodecImage::CopyTexImage(unsigned target) {
if (target != GL_TEXTURE_EXTERNAL_OES)
return false;

// Verify that the currently bound texture is the right one. If we're not
// copying to a Texture that shares our service_id, then we can't do much.
// This will force a copy.
// TODO(liberato): Fall back to a copy that uses the texture matrix.
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())
return false;

// Attach the surface texture to our GL context if needed.
// If the surface texture isn't attached yet, then attach it. Note that this
// will be to the texture in |shared_state_|, because of the checks above.
if (!shared_state_->surface_texture_is_attached())
AttachSurfaceTextureToContext();

// Make sure that we have the right image in the front buffer.
UpdateSurfaceTexture();

InstallTextureMatrix();

// TODO(liberato): Handle the texture matrix properly.
// Either we can update the shader with it or we can move all of the logic
// to updateTexImage() to the right place in the cc to send it to the shader.
// For now, we just skip it. crbug.com/530681
// 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
// texture id, so we can skip preserving it if the right context is current.
UpdateSurfaceTexture(kDontRestoreBindings);

// By setting image state to UNBOUND instead of COPIED we ensure that
// CopyTexImage() is called each time the surface texture is used for drawing.
// It would be nice if we could do this via asking for the currently bound
// Texture, but the active unit never seems to change.
texture_->SetLevelImage(GL_TEXTURE_EXTERNAL_OES, 0, this,
gpu::gles2::Texture::UNBOUND);
texture_->SetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES, 0, this,
gpu::gles2::Texture::UNBOUND);

return true;
}
Expand Down Expand Up @@ -123,11 +118,11 @@ void AVDACodecImage::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
uint64_t process_tracing_id,
const std::string& dump_name) {}

void AVDACodecImage::UpdateSurfaceTexture() {
void AVDACodecImage::UpdateSurfaceTexture(RestoreBindingsMode mode) {
DCHECK(surface_texture_);

// Render via the media codec if needed.
if (codec_buffer_index_ == kInvalidCodecBufferIndex || !media_codec_)
if (!IsCodecBufferOutstanding())
return;

// The decoder buffer is still pending.
Expand All @@ -142,12 +137,21 @@ void AVDACodecImage::UpdateSurfaceTexture() {
codec_buffer_index_ = kInvalidCodecBufferIndex;

// Swap the rendered image to the front.
scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
if (!shared_state_->context()->IsCurrent(NULL)) {
scoped_make_current.reset(new ui::ScopedMakeCurrent(
shared_state_->context(), shared_state_->surface()));
}
scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current = MakeCurrentIfNeeded();

// If we changed contexts, then we always want to restore it, since the caller
// doesn't know that we're switching contexts.
if (scoped_make_current)
mode = kDoRestoreBindings;

// Save the current binding if requested.
GLint bound_service_id = 0;
if (mode == kDoRestoreBindings)
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);

surface_texture_->UpdateTexImage();
if (mode == kDoRestoreBindings)
glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id);

// Helpfully, this is already column major.
surface_texture_->GetTransformMatrix(gl_matrix_);
Expand Down Expand Up @@ -176,6 +180,8 @@ void AVDACodecImage::SetTexture(gpu::gles2::Texture* texture) {
void AVDACodecImage::AttachSurfaceTextureToContext() {
DCHECK(surface_texture_);

// We assume that the currently bound texture is the intended one.

// Attach the surface texture to the first context we're bound on, so that
// no context switch is needed later.
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Expand All @@ -187,38 +193,48 @@ void AVDACodecImage::AttachSurfaceTextureToContext() {
// We could do this earlier, but SurfaceTexture has context affinity, and we
// don't want to require a context switch.
surface_texture_->AttachToGLContext();
shared_state_->did_attach_surface_texture();
shared_state_->DidAttachSurfaceTexture();
}

void AVDACodecImage::InstallTextureMatrix() {
DCHECK(surface_texture_);
scoped_ptr<ui::ScopedMakeCurrent> AVDACodecImage::MakeCurrentIfNeeded() {
DCHECK(shared_state_->context());
scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
if (!shared_state_->context()->IsCurrent(NULL)) {
scoped_make_current.reset(new ui::ScopedMakeCurrent(
shared_state_->context(), shared_state_->surface()));
}

// glUseProgram() has been run already -- just modify the uniform.
// Updating this via VideoFrameProvider::Client::DidUpdateMatrix() would
// be a better solution, except that we'd definitely miss a frame at this
// point in drawing.
// Our current method assumes that we'll end up being a stream resource,
// and that the program has a texMatrix uniform that does what we want.
if (need_shader_info_) {
GLint program_id = -1;
glGetIntegerv(GL_CURRENT_PROGRAM, &program_id);

if (program_id >= 0) {
// This is memorized from cc/output/shader.cc .
const char* uniformName = "texMatrix";

// Within unittests this value may be -1.
texmatrix_uniform_location_ =
glGetUniformLocation(program_id, uniformName);
}
return scoped_make_current;
}

// Only try once.
need_shader_info_ = false;
void AVDACodecImage::GetTextureMatrix(float matrix[16]) {
if (IsCodecBufferOutstanding() && shared_state_ && surface_texture_) {
// Our current matrix may be stale. Update it if possible.
if (!shared_state_->surface_texture_is_attached()) {
// Don't attach the surface texture permanently. Perhaps we should
// just attach the surface texture in avda and be done with it.
GLuint service_id = 0;
glGenTextures(1, &service_id);
GLint bound_service_id = 0;
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, service_id);
AttachSurfaceTextureToContext();
UpdateSurfaceTexture(kDontRestoreBindings);
// Detach the surface texture, which deletes the generated texture.
surface_texture_->DetachFromGLContext();
shared_state_->DidDetachSurfaceTexture();
glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id);
} else {
// Surface texture is already attached, so just update it.
UpdateSurfaceTexture(kDoRestoreBindings);
}
}

if (texmatrix_uniform_location_ >= 0) {
glUniformMatrix4fv(texmatrix_uniform_location_, 1, false, gl_matrix_);
}
memcpy(matrix, gl_matrix_, sizeof(gl_matrix_));
}

bool AVDACodecImage::IsCodecBufferOutstanding() const {
return codec_buffer_index_ != kInvalidCodecBufferIndex && media_codec_;
}

} // namespace content
46 changes: 31 additions & 15 deletions content/common/gpu/media/avda_codec_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@

#include "base/macros.h"
#include "content/common/gpu/media/avda_shared_state.h"
#include "ui/gl/gl_image.h"
#include "gpu/command_buffer/service/gl_stream_texture_image.h"

namespace ui {
class ScopedMakeCurrent;
}

namespace content {

// GLImage that renders MediaCodec buffers to a SurfaceTexture or SurfaceView as
// needed in order to draw them.
class AVDACodecImage : public gl::GLImage {
class AVDACodecImage : public gpu::gles2::GLStreamTextureImage {
public:
AVDACodecImage(const scoped_refptr<AVDASharedState>&,
media::VideoCodecBridge* codec,
Expand Down Expand Up @@ -44,6 +48,8 @@ class AVDACodecImage : public gl::GLImage {
void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
uint64_t process_tracing_id,
const std::string& dump_name) override;
// gpu::gles2::GLStreamTextureMatrix implementation
void GetTextureMatrix(float xform[16]) override;

public:
// Decoded buffer index that has the image for us to display.
Expand All @@ -63,15 +69,31 @@ class AVDACodecImage : public gl::GLImage {
private:
enum { kInvalidCodecBufferIndex = -1 };

// Make sure that the surface texture's front buffer is current.
void UpdateSurfaceTexture();

// Attach the surface texture to our GL context, with a texture that we
// create for it.
// Make sure that the surface texture's front buffer is current. This will
// save / restore the current context. It will optionally restore the texture
// bindings in the surface texture's context, based on |mode|. This is
// intended as a hint if we don't need to change contexts. If we do need to
// change contexts, then we'll always preserve the texture bindings in the
// both contexts. In other words, the caller is telling us whether it's
// okay to change the binding in the current context.
enum RestoreBindingsMode { kDontRestoreBindings, kDoRestoreBindings };
void UpdateSurfaceTexture(RestoreBindingsMode mode);

// Attach the surface texture to our GL context to whatever texture is bound
// on the active unit.
void AttachSurfaceTextureToContext();

// Install the current texture matrix into the shader.
void InstallTextureMatrix();
// Make shared_state_->context() current if it isn't already.
scoped_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded();

// Return whether or not the current context is in the same share group as
// |surface_texture_|'s client texture.
// TODO(liberato): is this needed?
bool IsCorrectShareGroup() const;

// Return whether there is a codec buffer that we haven't rendered yet. Will
// return false also if there's no codec or we otherwise can't update.
bool IsCodecBufferOutstanding() const;

// Shared state between the AVDA and all AVDACodecImages.
scoped_refptr<AVDASharedState> shared_state_;
Expand Down Expand Up @@ -99,12 +121,6 @@ class AVDACodecImage : public gl::GLImage {
// The texture that we're attached to.
gpu::gles2::Texture* texture_;

// Have we cached |texmatrix_uniform_location_| yet?
bool need_shader_info_;

// Uniform ID of the texture matrix in the shader.
GLint texmatrix_uniform_location_;

// Texture matrix of the front buffer of the surface texture.
float gl_matrix_[16];

Expand Down
8 changes: 7 additions & 1 deletion content/common/gpu/media/avda_shared_state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ void AVDASharedState::WaitForFrameAvailable() {
frame_available_event_.TimedWait(max_wait_time);
}

void AVDASharedState::did_attach_surface_texture() {
void AVDASharedState::DidAttachSurfaceTexture() {
context_ = gfx::GLContext::GetCurrent();
surface_ = gfx::GLSurface::GetCurrent();
DCHECK(context_);
Expand All @@ -37,4 +37,10 @@ void AVDASharedState::did_attach_surface_texture() {
surface_texture_is_attached_ = true;
}

void AVDASharedState::DidDetachSurfaceTexture() {
context_ = nullptr;
surface_ = nullptr;
surface_texture_is_attached_ = false;
}

} // namespace content
11 changes: 10 additions & 1 deletion content/common/gpu/media/avda_shared_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,19 @@ class AVDASharedState : public base::RefCounted<AVDASharedState> {
return surface_texture_is_attached_;
}

// TODO(liberato): move the surface texture here and make these calls
// attach / detach it also. There are several changes going on in avda
// concurrently, so I don't want to change that until the dust settles.
// AVDACodecImage would no longer hold the surface texture.

// Call this when the SurfaceTexture is attached to a GL context. This will
// update surface_texture_is_attached(), and set the context() and surface()
// to match.
void did_attach_surface_texture();
void DidAttachSurfaceTexture();

// Call this when the SurfaceTexture is detached from its GL context. This
// will cause us to forget the last binding.
void DidDetachSurfaceTexture();

private:
// Platform gl texture Id for |surface_texture_|. This will be zero if
Expand Down
Loading

0 comments on commit df64f2c

Please sign in to comment.