summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/common/BUILD.gn1
-rw-r--r--content/common/gpu/media/android_copying_backing_strategy.cc3
-rw-r--r--content/common/gpu/media/android_copying_backing_strategy.h3
-rw-r--r--content/common/gpu/media/android_deferred_rendering_backing_strategy.cc50
-rw-r--r--content/common/gpu/media/android_deferred_rendering_backing_strategy.h3
-rw-r--r--content/common/gpu/media/android_video_decode_accelerator.cc6
-rw-r--r--content/common/gpu/media/android_video_decode_accelerator.h3
-rw-r--r--content/common/gpu/media/avda_codec_image.cc101
-rw-r--r--content/common/gpu/media/avda_codec_image.h3
-rw-r--r--content/common/gpu/media/avda_shared_state.cc36
-rw-r--r--content/common/gpu/media/avda_shared_state.h42
-rw-r--r--content/content_common.gypi1
-rw-r--r--gpu/command_buffer/service/gl_state_restorer_impl.cc5
-rw-r--r--gpu/command_buffer/service/gl_state_restorer_impl.h1
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc39
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.h1
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_mock.h1
-rw-r--r--gpu/command_buffer/service/texture_manager.cc55
-rw-r--r--gpu/command_buffer/service/texture_manager.h31
-rw-r--r--gpu/command_buffer/service/texture_manager_unittest.cc45
-rw-r--r--ui/gl/gl_state_restorer.h1
21 files changed, 330 insertions, 101 deletions
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index bd80d23..0a7e72e 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -360,6 +360,7 @@ source_set("common") {
"gpu/media/avda_codec_image.cc",
"gpu/media/avda_codec_image.h",
"gpu/media/avda_return_on_failure.h",
+ "gpu/media/avda_shared_state.cc",
"gpu/media/avda_shared_state.h",
"gpu/media/avda_state_provider.h",
]
diff --git a/content/common/gpu/media/android_copying_backing_strategy.cc b/content/common/gpu/media/android_copying_backing_strategy.cc
index b58cf50..f80a16f 100644
--- a/content/common/gpu/media/android_copying_backing_strategy.cc
+++ b/content/common/gpu/media/android_copying_backing_strategy.cc
@@ -32,12 +32,13 @@ void AndroidCopyingBackingStrategy::Initialize(
}
void AndroidCopyingBackingStrategy::Cleanup(
+ bool have_context,
const AndroidVideoDecodeAccelerator::OutputBufferMap&) {
DCHECK(state_provider_->ThreadChecker().CalledOnValidThread());
if (copier_)
copier_->Destroy();
- if (surface_texture_id_)
+ if (surface_texture_id_ && have_context)
glDeleteTextures(1, &surface_texture_id_);
}
diff --git a/content/common/gpu/media/android_copying_backing_strategy.h b/content/common/gpu/media/android_copying_backing_strategy.h
index ecb50b2..17b096a 100644
--- a/content/common/gpu/media/android_copying_backing_strategy.h
+++ b/content/common/gpu/media/android_copying_backing_strategy.h
@@ -33,7 +33,8 @@ class CONTENT_EXPORT AndroidCopyingBackingStrategy
// AndroidVideoDecodeAccelerator::BackingStrategy
void Initialize(AVDAStateProvider*) override;
- void Cleanup(const AndroidVideoDecodeAccelerator::OutputBufferMap&) override;
+ void Cleanup(bool have_context,
+ const AndroidVideoDecodeAccelerator::OutputBufferMap&) override;
uint32_t GetTextureTarget() const override;
scoped_refptr<gfx::SurfaceTexture> CreateSurfaceTexture() override;
void UseCodecBufferForPictureBuffer(int32_t codec_buffer_index,
diff --git a/content/common/gpu/media/android_deferred_rendering_backing_strategy.cc b/content/common/gpu/media/android_deferred_rendering_backing_strategy.cc
index 6b05495..0bec437 100644
--- a/content/common/gpu/media/android_deferred_rendering_backing_strategy.cc
+++ b/content/common/gpu/media/android_deferred_rendering_backing_strategy.cc
@@ -29,17 +29,28 @@ void AndroidDeferredRenderingBackingStrategy::Initialize(
AVDAStateProvider* state_provider) {
state_provider_ = state_provider;
shared_state_ = new AVDASharedState();
+
+ // Create a texture for the SurfaceTexture to use. We don't attach it here
+ // so that it gets attached in the compositor gl context in the common case.
+ GLuint service_id = 0;
+ glGenTextures(1, &service_id);
+ DCHECK(service_id);
+ shared_state_->set_surface_texture_service_id(service_id);
}
void AndroidDeferredRenderingBackingStrategy::Cleanup(
+ bool have_context,
const AndroidVideoDecodeAccelerator::OutputBufferMap& buffers) {
- for (const std::pair<int, media::PictureBuffer>& entry : buffers) {
- AVDACodecImage* avImage = GetImageForPicture(entry.second);
- if (avImage) {
- avImage->SetMediaCodecBufferIndex(-1);
- avImage->SetMediaCodec(nullptr);
- }
- }
+ // 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)
+ SetImageForPicture(entry.second, nullptr);
+
+ // 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);
}
uint32_t AndroidDeferredRenderingBackingStrategy::GetTextureTarget() const {
@@ -98,12 +109,22 @@ void AndroidDeferredRenderingBackingStrategy::SetImageForPicture(
size.width(), size.height(), 1, 0, GL_RGBA,
GL_UNSIGNED_BYTE, gfx::Rect());
- texture_manager->SetLevelImage(texture_ref, GetTextureTarget(), 0,
- image.get(), gpu::gles2::Texture::UNBOUND);
+ // Override the texture's service_id, so that it will use the one that
+ // will be / is attached to the SurfaceTexture.
+ DCHECK(shared_state_->surface_texture_service_id());
+ texture_ref->texture()->SetUnownedServiceId(
+ shared_state_->surface_texture_service_id());
static_cast<AVDACodecImage*>(image.get())
->setTexture(texture_ref->texture());
+ } else {
+ // Clear the unowned service_id, so that this texture is no longer going
+ // to depend on the surface texture at all.
+ texture_ref->texture()->SetUnownedServiceId(0);
}
+
+ texture_manager->SetLevelImage(texture_ref, GetTextureTarget(), 0,
+ image.get(), gpu::gles2::Texture::UNBOUND);
}
void AndroidDeferredRenderingBackingStrategy::UseCodecBufferForPictureBuffer(
@@ -162,13 +183,10 @@ void AndroidDeferredRenderingBackingStrategy::DismissOnePictureBuffer(
// release it.
ReleaseCodecBufferForPicture(picture_buffer);
- // Paranoia. The texture will be dropped anyway, causing the picture to be
- // deleted then.
- SetImageForPicture(picture_buffer, 0);
-
- // TODO(liberato): If we really want to re-use a picture buffer's texture as
- // the surface texture's consumer texture id, then we could manage
- // re-assigning it here.
+ // This makes sure that the Texture no longer refers to the codec or to the
+ // SurfaceTexture's service_id. That's important, so that it doesn't refer
+ // to the texture by name after we've deleted it.
+ SetImageForPicture(picture_buffer, nullptr);
}
void AndroidDeferredRenderingBackingStrategy::CodecChanged(
diff --git a/content/common/gpu/media/android_deferred_rendering_backing_strategy.h b/content/common/gpu/media/android_deferred_rendering_backing_strategy.h
index 7fc13eb..6fc1873 100644
--- a/content/common/gpu/media/android_deferred_rendering_backing_strategy.h
+++ b/content/common/gpu/media/android_deferred_rendering_backing_strategy.h
@@ -38,7 +38,8 @@ class CONTENT_EXPORT AndroidDeferredRenderingBackingStrategy
// AndroidVideoDecodeAccelerator::BackingStrategy
void Initialize(AVDAStateProvider*) override;
- void Cleanup(const AndroidVideoDecodeAccelerator::OutputBufferMap&) override;
+ void Cleanup(bool have_context,
+ const AndroidVideoDecodeAccelerator::OutputBufferMap&) override;
uint32_t GetTextureTarget() const override;
scoped_refptr<gfx::SurfaceTexture> CreateSurfaceTexture() override;
void UseCodecBufferForPictureBuffer(int32_t codec_buffer_index,
diff --git a/content/common/gpu/media/android_video_decode_accelerator.cc b/content/common/gpu/media/android_video_decode_accelerator.cc
index b4cb0a2..832d8dc 100644
--- a/content/common/gpu/media/android_video_decode_accelerator.cc
+++ b/content/common/gpu/media/android_video_decode_accelerator.cc
@@ -755,7 +755,11 @@ void AndroidVideoDecodeAccelerator::Reset() {
void AndroidVideoDecodeAccelerator::Destroy() {
DCHECK(thread_checker_.CalledOnValidThread());
- strategy_->Cleanup(output_picture_buffers_);
+ bool have_context = make_context_current_.Run();
+ if (!have_context)
+ LOG(WARNING) << "Failed make GL context current for Destroy, continuing.";
+
+ strategy_->Cleanup(have_context, output_picture_buffers_);
// If we have an OnFrameAvailable handler, tell it that we're going away.
if (on_frame_available_handler_) {
diff --git a/content/common/gpu/media/android_video_decode_accelerator.h b/content/common/gpu/media/android_video_decode_accelerator.h
index 6432f7c..1dd6816 100644
--- a/content/common/gpu/media/android_video_decode_accelerator.h
+++ b/content/common/gpu/media/android_video_decode_accelerator.h
@@ -53,7 +53,8 @@ class CONTENT_EXPORT AndroidVideoDecodeAccelerator
// Called before the AVDA does any Destroy() work. This will be
// the last call that the BackingStrategy receives.
- virtual void Cleanup(const OutputBufferMap& buffer_map) = 0;
+ virtual void Cleanup(bool have_context,
+ const OutputBufferMap& buffer_map) = 0;
// Return the GL texture target that the PictureBuffer textures use.
virtual uint32_t GetTextureTarget() const = 0;
diff --git a/content/common/gpu/media/avda_codec_image.cc b/content/common/gpu/media/avda_codec_image.cc
index e77d15d..1df753d 100644
--- a/content/common/gpu/media/avda_codec_image.cc
+++ b/content/common/gpu/media/avda_codec_image.cc
@@ -38,18 +38,7 @@ AVDACodecImage::AVDACodecImage(
AVDACodecImage::~AVDACodecImage() {}
-void AVDACodecImage::Destroy(bool have_context) {
- // If the SurfaceTexture is using our texture, then detach from it.
- if (detach_surface_texture_on_destruction_) {
- // We don't really care if we have no context, since it doesn't
- // matter if the texture is destroyed here or not. As long as the
- // surface texture doesn't try to delete this handle later (after
- // it might have been reused), it's fine. Somebody else will delete
- // our texture when the picture buffer is destroyed.
- surface_texture_->DetachFromGLContext();
- shared_state_->set_surface_texture_service_id(0);
- }
-}
+void AVDACodecImage::Destroy(bool have_context) {}
gfx::Size AVDACodecImage::GetSize() {
return size_;
@@ -69,28 +58,24 @@ bool AVDACodecImage::CopyTexImage(unsigned target) {
if (target != GL_TEXTURE_EXTERNAL_OES)
return false;
- // Have we bound the SurfaceTexture's texture handle to the active
- // texture unit yet?
- bool bound_texture = 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);
+ if (bound_service_id != shared_state_->surface_texture_service_id())
+ return false;
// Attach the surface texture to our GL context if needed.
- if (!shared_state_->surface_texture_service_id()) {
+ if (!shared_state_->surface_texture_is_attached())
AttachSurfaceTextureToContext();
- bound_texture = true;
- }
// Make sure that we have the right image in the front buffer.
- bound_texture |= UpdateSurfaceTexture();
+ UpdateSurfaceTexture();
InstallTextureMatrix();
- // Sneakily bind the ST texture handle in the real GL context.
- // If we called UpdateTexImage() to update the ST front buffer, then we can
- // skip this. Since one draw/frame is the common case, we optimize for it.
- if (!bound_texture)
- glBindTexture(GL_TEXTURE_EXTERNAL_OES,
- shared_state_->surface_texture_service_id());
-
// 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.
@@ -124,36 +109,32 @@ void AVDACodecImage::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
uint64_t process_tracing_id,
const std::string& dump_name) {}
-bool AVDACodecImage::UpdateSurfaceTexture() {
+void AVDACodecImage::UpdateSurfaceTexture() {
// Render via the media codec if needed.
- if (codec_buffer_index_ > -1 && media_codec_) {
- // We have been given a codec buffer to render, so render it.
- // We might want to ask the avda to release any buffers that come
- // before us without rendering, just for good measure. However,
- // to prevent doing lots of work on the drawing path, we skip it.
-
- // The decoder buffer was still pending.
- // This must be synchronous, so wait for OnFrameAvailable.
- media_codec_->ReleaseOutputBuffer(codec_buffer_index_, true);
- {
- SCOPED_UMA_HISTOGRAM_TIMER("Media.AvdaCodecImage.WaitTimeForFrame");
- shared_state_->WaitForFrameAvailable();
- }
-
- // Don't bother to check if we're rendered again.
- codec_buffer_index_ = -1;
-
- // Swap the rendered image to the front.
- surface_texture_->UpdateTexImage();
+ if (codec_buffer_index_ <= -1 || !media_codec_)
+ return;
+
+ // The decoder buffer is still pending.
+ // This must be synchronous, so wait for OnFrameAvailable.
+ media_codec_->ReleaseOutputBuffer(codec_buffer_index_, true);
+ {
+ SCOPED_UMA_HISTOGRAM_TIMER("Media.AvdaCodecImage.WaitTimeForFrame");
+ shared_state_->WaitForFrameAvailable();
+ }
- // Helpfully, this is already column major.
- surface_texture_->GetTransformMatrix(gl_matrix_);
+ // Don't bother to check if we're rendered again.
+ codec_buffer_index_ = -1;
- // UpdateTexImage() binds the ST's texture.
- return true;
+ // 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()));
}
+ surface_texture_->UpdateTexImage();
- return false;
+ // Helpfully, this is already column major.
+ surface_texture_->GetTransformMatrix(gl_matrix_);
}
void AVDACodecImage::SetMediaCodecBufferIndex(int buffer_index) {
@@ -177,25 +158,19 @@ void AVDACodecImage::setTexture(gpu::gles2::Texture* texture) {
}
void AVDACodecImage::AttachSurfaceTextureToContext() {
- GLint surface_texture_service_id;
- // Use the PictureBuffer's texture. We could also generate a new texture
- // here, but cleaning it up is problematic.
- glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &surface_texture_service_id);
- DCHECK(surface_texture_service_id);
-
- // Attach to our service id.
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, surface_texture_service_id);
+ // 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);
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);
// The surface texture is already detached, so just attach it.
+ // 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_->set_surface_texture_service_id(surface_texture_service_id);
- detach_surface_texture_on_destruction_ = true;
-
- // We do not restore the GL state here.
+ shared_state_->did_attach_surface_texture();
}
void AVDACodecImage::InstallTextureMatrix() {
diff --git a/content/common/gpu/media/avda_codec_image.h b/content/common/gpu/media/avda_codec_image.h
index 000e5eb..ef0456a 100644
--- a/content/common/gpu/media/avda_codec_image.h
+++ b/content/common/gpu/media/avda_codec_image.h
@@ -62,8 +62,7 @@ class AVDACodecImage : public gl::GLImage {
private:
// Make sure that the surface texture's front buffer is current.
- // Returns true if the ST's texture was bound to the active unit.
- bool UpdateSurfaceTexture();
+ void UpdateSurfaceTexture();
// Attach the surface texture to our GL context, with a texture that we
// create for it.
diff --git a/content/common/gpu/media/avda_shared_state.cc b/content/common/gpu/media/avda_shared_state.cc
new file mode 100644
index 0000000..c182bf0
--- /dev/null
+++ b/content/common/gpu/media/avda_shared_state.cc
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/gpu/media/avda_shared_state.h"
+
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/scoped_make_current.h"
+
+namespace content {
+
+AVDASharedState::AVDASharedState()
+ : surface_texture_service_id_(0),
+ frame_available_event_(false, false),
+ surface_texture_is_attached_(false) {}
+
+AVDASharedState::~AVDASharedState() {}
+
+void AVDASharedState::SignalFrameAvailable() {
+ frame_available_event_.Signal();
+}
+
+void AVDASharedState::WaitForFrameAvailable() {
+ frame_available_event_.Wait();
+}
+
+void AVDASharedState::did_attach_surface_texture() {
+ context_ = gfx::GLContext::GetCurrent();
+ surface_ = gfx::GLSurface::GetCurrent();
+ DCHECK(context_);
+ DCHECK(surface_);
+
+ surface_texture_is_attached_ = true;
+}
+
+} // namespace content
diff --git a/content/common/gpu/media/avda_shared_state.h b/content/common/gpu/media/avda_shared_state.h
index f0996f6..eb62681 100644
--- a/content/common/gpu/media/avda_shared_state.h
+++ b/content/common/gpu/media/avda_shared_state.h
@@ -7,8 +7,11 @@
#include "base/synchronization/waitable_event.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
+#include "media/base/android/media_codec_bridge.h"
#include "media/base/android/sdk_media_codec_bridge.h"
+#include "ui/gl/gl_context.h"
#include "ui/gl/gl_image.h"
+#include "ui/gl/gl_surface.h"
namespace gfx {
class SurfaceTexture;
@@ -18,36 +21,59 @@ namespace content {
// Shared state to allow communication between the AVDA and the
// GLImages that configure GL for drawing the frames.
-// TODO(liberato): If the deferred backing strategy owned the service id, then
-// the shared state could be removed entirely. However, I'm not yet sure if
-// there's an issue with virtual gl contexts.
class AVDASharedState : public base::RefCounted<AVDASharedState> {
public:
- AVDASharedState()
- : surface_texture_service_id_(0), frame_available_event_(false, false) {}
+ AVDASharedState();
GLint surface_texture_service_id() const {
return surface_texture_service_id_;
}
+ // Set the SurfaceTexture's client texture name, which the SurfaceTexture
+ // might not know about yet (see surface_texture_is_attached()).
void set_surface_texture_service_id(GLint id) {
surface_texture_service_id_ = id;
}
- void SignalFrameAvailable() { frame_available_event_.Signal(); }
+ // Signal the "frame available" event. This may be called from any thread.
+ void SignalFrameAvailable();
- void WaitForFrameAvailable() { frame_available_event_.Wait(); }
+ void WaitForFrameAvailable();
+
+ // Context that the surface texture is bound to, or nullptr if it is not in
+ // the attached state.
+ gfx::GLContext* context() const { return context_.get(); }
+
+ gfx::GLSurface* surface() const { return surface_.get(); }
+
+ bool surface_texture_is_attached() const {
+ return surface_texture_is_attached_;
+ }
+
+ // 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();
private:
// Platform gl texture Id for |surface_texture_|. This will be zero if
// and only if |texture_owner_| is null.
+ // TODO(liberato): This should be GLuint, but we don't seem to have the type.
GLint surface_texture_service_id_;
// For signalling OnFrameAvailable().
base::WaitableEvent frame_available_event_;
+ // True if and only if the surface texture is currently attached.
+ bool surface_texture_is_attached_;
+
+ // Context and surface that the surface texture is attached to, if it is
+ // currently attached.
+ scoped_refptr<gfx::GLContext> context_;
+ scoped_refptr<gfx::GLSurface> surface_;
+
protected:
- virtual ~AVDASharedState() {}
+ virtual ~AVDASharedState();
private:
friend class base::RefCounted<AVDASharedState>;
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 806f63f..f18b3b4 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -808,6 +808,7 @@
'common/gpu/media/avda_codec_image.cc',
'common/gpu/media/avda_codec_image.h',
'common/gpu/media/avda_return_on_failure.h',
+ 'common/gpu/media/avda_shared_state.cc',
'common/gpu/media/avda_shared_state.h',
'common/gpu/media/avda_state_provider.h',
],
diff --git a/gpu/command_buffer/service/gl_state_restorer_impl.cc b/gpu/command_buffer/service/gl_state_restorer_impl.cc
index c132ef8..887a14a 100644
--- a/gpu/command_buffer/service/gl_state_restorer_impl.cc
+++ b/gpu/command_buffer/service/gl_state_restorer_impl.cc
@@ -41,6 +41,11 @@ void GLStateRestorerImpl::RestoreActiveTextureUnitBinding(unsigned int target) {
decoder_->RestoreActiveTextureUnitBinding(target);
}
+void GLStateRestorerImpl::RestoreAllExternalTextureBindingsIfNeeded() {
+ DCHECK(decoder_.get());
+ decoder_->RestoreAllExternalTextureBindingsIfNeeded();
+}
+
void GLStateRestorerImpl::RestoreFramebufferBindings() {
DCHECK(decoder_.get());
decoder_->RestoreFramebufferBindings();
diff --git a/gpu/command_buffer/service/gl_state_restorer_impl.h b/gpu/command_buffer/service/gl_state_restorer_impl.h
index 494a8cb..760fde6 100644
--- a/gpu/command_buffer/service/gl_state_restorer_impl.h
+++ b/gpu/command_buffer/service/gl_state_restorer_impl.h
@@ -29,6 +29,7 @@ class GPU_EXPORT GLStateRestorerImpl : public gfx::GLStateRestorer {
void RestoreState(const gfx::GLStateRestorer* prev_state) override;
void RestoreAllTextureUnitBindings() override;
void RestoreActiveTextureUnitBinding(unsigned int target) override;
+ void RestoreAllExternalTextureBindingsIfNeeded() override;
void RestoreFramebufferBindings() override;
void PauseQueries() override;
void ResumeQueries() override;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 515a825..9538614 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -1878,6 +1878,10 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient {
GLint location,
const std::string& name);
+ // If |texture_manager_version_| doesn't match the current version, then this
+ // will rebind all external textures to match their current service_id.
+ void RestoreAllExternalTextureBindingsIfNeeded() override;
+
// Generate a member function prototype for each command in an automated and
// typesafe way.
#define GLES2_CMD_OP(name) \
@@ -2074,6 +2078,11 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient {
// A table of CommandInfo for all the commands.
static const CommandInfo command_info[kNumCommands - kStartPoint];
+ // Most recent generation of the TextureManager. If this no longer matches
+ // the current generation when our context becomes current, then we'll rebind
+ // all the textures to stay up-to-date with Texture::service_id() changes.
+ uint32_t texture_manager_service_id_generation_;
+
bool force_shader_name_hashing_for_test;
GLfloat line_width_range_[2];
@@ -2557,6 +2566,7 @@ GLES2DecoderImpl::GLES2DecoderImpl(ContextGroup* group)
validation_texture_(0),
validation_fbo_multisample_(0),
validation_fbo_(0),
+ texture_manager_service_id_generation_(0),
force_shader_name_hashing_for_test(false) {
DCHECK(group);
}
@@ -3592,6 +3602,9 @@ bool GLES2DecoderImpl::MakeCurrent() {
framebuffer_state_.clear_state_dirty = true;
+ // Rebind textures if the service ids may have changed.
+ RestoreAllExternalTextureBindingsIfNeeded();
+
return true;
}
@@ -15579,6 +15592,32 @@ error::Error GLES2DecoderImpl::HandleProgramPathFragmentInputGenCHROMIUM(
return error::kNoError;
}
+void GLES2DecoderImpl::RestoreAllExternalTextureBindingsIfNeeded() {
+ if (texture_manager()->GetServiceIdGeneration() ==
+ texture_manager_service_id_generation_)
+ return;
+
+ // Texture manager's version has changed, so rebind all external textures
+ // in case their service ids have changed.
+ for (unsigned texture_unit_index = 0;
+ texture_unit_index < state_.texture_units.size(); texture_unit_index++) {
+ TextureUnit& texture_unit = state_.texture_units[texture_unit_index];
+ if (texture_unit.bind_target != GL_TEXTURE_EXTERNAL_OES)
+ continue;
+
+ if (TextureRef* texture_ref =
+ texture_unit.bound_texture_external_oes.get()) {
+ glActiveTexture(GL_TEXTURE0 + texture_unit_index);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_ref->service_id());
+ }
+ }
+
+ glActiveTexture(GL_TEXTURE0 + state_.active_texture_unit);
+
+ texture_manager_service_id_generation_ =
+ texture_manager()->GetServiceIdGeneration();
+}
+
// Include the auto-generated part of this file. We split this because it means
// we can easily edit the non-auto generated parts right here in this file
// instead of having to edit some template or the code generator.
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h
index 9b7cade..3a4825f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.h
@@ -174,6 +174,7 @@ class GPU_EXPORT GLES2Decoder : public base::SupportsWeakPtr<GLES2Decoder>,
virtual void RestoreProgramBindings() const = 0;
virtual void RestoreTextureState(unsigned service_id) const = 0;
virtual void RestoreTextureUnitBindings(unsigned unit) const = 0;
+ virtual void RestoreAllExternalTextureBindingsIfNeeded() = 0;
virtual void ClearAllAttributes() const = 0;
virtual void RestoreAllAttributes() const = 0;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
index 7cc02c9..bc954f8 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -72,6 +72,7 @@ class MockGLES2Decoder : public GLES2Decoder {
RestoreAllTextureUnitBindings, void(const ContextState* state));
MOCK_CONST_METHOD1(
RestoreActiveTextureUnitBinding, void(unsigned int target));
+ MOCK_METHOD0(RestoreAllExternalTextureBindingsIfNeeded, void());
MOCK_CONST_METHOD0(RestoreBufferBindings, void());
MOCK_CONST_METHOD0(RestoreFramebufferBindings, void());
MOCK_CONST_METHOD0(RestoreGlobalState, void());
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 82fab48..9345bb8 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -25,7 +25,9 @@
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/memory_tracking.h"
+#include "ui/gl/gl_context.h"
#include "ui/gl/gl_implementation.h"
+#include "ui/gl/gl_state_restorer.h"
#include "ui/gl/gl_version_info.h"
#include "ui/gl/trace_util.h"
@@ -314,6 +316,7 @@ Texture::Texture(GLuint service_id)
: mailbox_manager_(NULL),
memory_tracking_ref_(NULL),
service_id_(service_id),
+ owned_service_id_(service_id),
cleared_(true),
num_uncleared_mips_(0),
num_npot_faces_(0),
@@ -342,8 +345,7 @@ Texture::Texture(GLuint service_id)
has_images_(false),
estimated_size_(0),
can_render_condition_(CAN_RENDER_ALWAYS),
- texture_max_anisotropy_initialized_(false) {
-}
+ texture_max_anisotropy_initialized_(false) {}
Texture::~Texture() {
if (mailbox_manager_)
@@ -367,10 +369,8 @@ void Texture::RemoveTextureRef(TextureRef* ref, bool have_context) {
size_t result = refs_.erase(ref);
DCHECK_EQ(result, 1u);
if (refs_.empty()) {
- if (have_context) {
- GLuint id = service_id();
- glDeleteTextures(1, &id);
- }
+ if (have_context)
+ glDeleteTextures(1, &owned_service_id_);
delete this;
} else if (memory_tracking_ref_ == NULL) {
// TODO(piman): tune ownership semantics for cross-context group shared
@@ -1302,6 +1302,7 @@ void Texture::SetLevelImage(GLenum target,
DCHECK_EQ(info.level, level);
info.image = image;
info.image_state = state;
+
UpdateCanRenderCondition();
UpdateHasImages();
}
@@ -1367,6 +1368,30 @@ void Texture::DumpLevelMemory(base::trace_event::ProcessMemoryDump* pmd,
}
}
+void Texture::SetUnownedServiceId(GLuint service_id) {
+ GLuint new_service_id = service_id;
+
+ // Take no action if this isn't an OES_EXTERNAL texture.
+ if (target_ && target_ != GL_TEXTURE_EXTERNAL_OES)
+ return;
+
+ if (!service_id)
+ new_service_id = owned_service_id_;
+
+ if (service_id_ != new_service_id) {
+ service_id_ = new_service_id;
+ IncrementManagerServiceIdGeneration();
+ if (gfx::GLContext* context = gfx::GLContext::GetCurrent()) {
+ // It would be preferable to pass in the decoder, and ask it to do this
+ // instead. However, there are several cases, such as TextureDefinition,
+ // that show up without a clear context owner. So, instead, we use the
+ // current state's state restorer.
+ if (gfx::GLStateRestorer* restorer = context->GetGLStateRestorer())
+ restorer->RestoreAllExternalTextureBindingsIfNeeded();
+ }
+ }
+}
+
TextureRef::TextureRef(TextureManager* manager,
GLuint client_id,
Texture* texture)
@@ -1426,7 +1451,8 @@ TextureManager::TextureManager(MemoryTracker* memory_tracker,
num_uncleared_mips_(0),
num_images_(0),
texture_count_(0),
- have_context_(true) {
+ have_context_(true),
+ current_service_id_generation_(0) {
for (int ii = 0; ii < kNumDefaultTextures; ++ii) {
black_texture_ids_[ii] = 0;
}
@@ -2627,5 +2653,20 @@ GLenum TextureManager::ExtractTypeFromStorageFormat(GLenum internalformat) {
}
}
+void Texture::IncrementManagerServiceIdGeneration() {
+ for (auto ref : refs_) {
+ TextureManager* manager = ref->manager();
+ manager->IncrementServiceIdGeneration();
+ }
+}
+
+uint32_t TextureManager::GetServiceIdGeneration() const {
+ return current_service_id_generation_;
+}
+
+void TextureManager::IncrementServiceIdGeneration() {
+ current_service_id_generation_++;
+}
+
} // namespace gles2
} // namespace gpu
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index 1a2b532..5a07d99 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -128,6 +128,12 @@ class GPU_EXPORT Texture {
service_id_ = service_id;
}
+ // Causes us to report |service_id| as our service id, but does not delete
+ // it when we are destroyed. Will rebind any OES_EXTERNAL texture units to
+ // our new service id in all contexts. If |service_id| is zero, then we
+ // revert to our original service id.
+ void SetUnownedServiceId(GLuint service_id);
+
// Returns the target this texure was first bound to or 0 if it has not
// been bound. Once a texture is bound to a specific target it can never be
// bound to a different target.
@@ -426,6 +432,15 @@ class GPU_EXPORT Texture {
void UpdateMaxLevel(GLint max_level);
void UpdateNumMipLevels();
+ // Increment the generation counter for all managers that have a reference to
+ // this texture.
+ void IncrementManagerServiceIdGeneration();
+
+ // Return the service id of the texture that we will delete when we are
+ // destroyed. Normally, this is the same as service_id(), unless it is
+ // overridden by SetUnownedServiceId.
+ GLuint owned_service_id() const { return owned_service_id_; }
+
MailboxManager* mailbox_manager_;
// Info about each face and level of texture.
@@ -442,6 +457,15 @@ class GPU_EXPORT Texture {
// The id of the texure
GLuint service_id_;
+ // The id of the texture that we are responsible for deleting. Normally,
+ // this is the same as service_id_, unless a call to SetUnownedServiceId
+ // overrides it. In that case, we'll use the overridden service id (stored
+ // in |service_id_|) for all purposes except deleting the texture name.
+ // Whoever calls SetUnownedServiceId is assumed to handle deleting that id,
+ // and only after we are either deleted or told to stop using it via
+ // another call to SetUnownedServiceId.
+ GLuint owned_service_id_;
+
// Whether all renderable mips of this texture have been cleared.
bool cleared_;
@@ -932,6 +956,11 @@ class GPU_EXPORT TextureManager : public base::trace_event::MemoryDumpProvider {
const gfx::Rect& rect2,
gfx::Rect* result);
+ // Get / set the current generation number of this manager. This generation
+ // number changes whenever the service_id of one or more Textures change.
+ uint32_t GetServiceIdGeneration() const;
+ void IncrementServiceIdGeneration();
+
private:
friend class Texture;
friend class TextureRef;
@@ -1008,6 +1037,8 @@ class GPU_EXPORT TextureManager : public base::trace_event::MemoryDumpProvider {
std::vector<DestructionObserver*> destruction_observers_;
+ uint32_t current_service_id_generation_;
+
DISALLOW_COPY_AND_ASSIGN(TextureManager);
};
diff --git a/gpu/command_buffer/service/texture_manager_unittest.cc b/gpu/command_buffer/service/texture_manager_unittest.cc
index 740e8f4..c7557ee 100644
--- a/gpu/command_buffer/service/texture_manager_unittest.cc
+++ b/gpu/command_buffer/service/texture_manager_unittest.cc
@@ -47,6 +47,9 @@ class TextureTestHelper {
static bool IsCubeComplete(const Texture* texture) {
return texture->cube_complete();
}
+ static GLuint owned_service_id(const Texture* texture) {
+ return texture->owned_service_id();
+ }
};
class TextureManagerTest : public GpuServiceTest {
@@ -467,6 +470,48 @@ TEST_F(TextureManagerTest, ValidForTargetNPOT) {
manager.Destroy(false);
}
+TEST_F(TextureManagerTest, OverrideServiceID) {
+ // Create a texture.
+ const GLuint kClientId = 1;
+ const GLuint kServiceId = 11;
+ manager_->CreateTexture(kClientId, kServiceId);
+ scoped_refptr<TextureRef> texture_ref(manager_->GetTexture(kClientId));
+ manager_->SetTarget(texture_ref.get(), GL_TEXTURE_EXTERNAL_OES);
+
+ Texture* texture = texture_ref->texture();
+ GLuint owned_service_id = TextureTestHelper::owned_service_id(texture);
+ GLuint service_id = texture->service_id();
+ // Initially, the texture should use the same service id that it owns.
+ EXPECT_EQ(owned_service_id, service_id);
+
+ // Override the service_id.
+ GLuint unowned_service_id = service_id + 1;
+ texture->SetUnownedServiceId(unowned_service_id);
+
+ // Make sure that service_id() changed but owned_service_id() didn't.
+ EXPECT_EQ(unowned_service_id, texture->service_id());
+ EXPECT_EQ(owned_service_id, TextureTestHelper::owned_service_id(texture));
+
+ // Undo the override.
+ texture->SetUnownedServiceId(0);
+
+ // The service IDs should be back as they were.
+ EXPECT_EQ(service_id, texture->service_id());
+ EXPECT_EQ(owned_service_id, TextureTestHelper::owned_service_id(texture));
+
+ // Override again, so that we can check delete behavior.
+ texture->SetUnownedServiceId(unowned_service_id);
+ EXPECT_EQ(unowned_service_id, texture->service_id());
+ EXPECT_EQ(owned_service_id, TextureTestHelper::owned_service_id(texture));
+
+ // Remove the texture. It should delete the texture id that it owns, even
+ // though it is overridden.
+ EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(owned_service_id)))
+ .Times(1)
+ .RetiresOnSaturation();
+ manager_->RemoveTexture(kClientId);
+}
+
class TextureTestBase : public GpuServiceTest {
public:
static const GLint kMaxTextureSize = 32;
diff --git a/ui/gl/gl_state_restorer.h b/ui/gl/gl_state_restorer.h
index dbeefff..236ed62 100644
--- a/ui/gl/gl_state_restorer.h
+++ b/ui/gl/gl_state_restorer.h
@@ -27,6 +27,7 @@ class GL_EXPORT GLStateRestorer {
virtual void RestoreState(const GLStateRestorer* prev_state) = 0;
virtual void RestoreAllTextureUnitBindings() = 0;
virtual void RestoreActiveTextureUnitBinding(unsigned int target) = 0;
+ virtual void RestoreAllExternalTextureBindingsIfNeeded() = 0;
virtual void RestoreFramebufferBindings() = 0;
virtual void PauseQueries() = 0;
virtual void ResumeQueries() = 0;