diff options
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; |