Commit ff17b0a9 authored by liberato@chromium.org's avatar liberato@chromium.org Committed by Commit Bot

Allow textures to outlive the MCVD that created them.

Previously, MCVD destroyed the textures that back the VideoFrames
when MCVD was destroyed.  When the pipeline suspends, this causes
any VideoFrame to become undrawable.

This CL adds TexturePool, which holds references to the textures
until all VideoFrames are destroyed.  While the implementation
isn't exactly a pool, we will be adding pool functionality to
it shortly.

This CL also introduces a wrapper for GL command buffer stubs, to
allow for easier testing.

Similarly, this CL adds a wrapper around TextureRef.

Bug: 737220
Test: TexturePoolTest
Cq-Include-Trybots: luci.chromium.try:linux_optional_gpu_tests_rel;master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: I3d9bd42b9f8d3782b3eb4ceca30388774dc4854b
Reviewed-on: https://chromium-review.googlesource.com/966782
Commit-Queue: Frank Liberato <liberato@chromium.org>
Reviewed-by: default avatarThomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545609}
parent 58b898fb
...@@ -166,6 +166,9 @@ component("gpu") { ...@@ -166,6 +166,9 @@ component("gpu") {
"android/codec_image_group.h", "android/codec_image_group.h",
"android/codec_wrapper.cc", "android/codec_wrapper.cc",
"android/codec_wrapper.h", "android/codec_wrapper.h",
"android/command_buffer_stub_wrapper.h",
"android/command_buffer_stub_wrapper_impl.cc",
"android/command_buffer_stub_wrapper_impl.h",
"android/content_video_view_overlay.cc", "android/content_video_view_overlay.cc",
"android/content_video_view_overlay.h", "android/content_video_view_overlay.h",
"android/content_video_view_overlay_allocator.cc", "android/content_video_view_overlay_allocator.cc",
...@@ -181,6 +184,10 @@ component("gpu") { ...@@ -181,6 +184,10 @@ component("gpu") {
"android/surface_chooser_helper.h", "android/surface_chooser_helper.h",
"android/surface_texture_gl_owner.cc", "android/surface_texture_gl_owner.cc",
"android/surface_texture_gl_owner.h", "android/surface_texture_gl_owner.h",
"android/texture_pool.cc",
"android/texture_pool.h",
"android/texture_wrapper.cc",
"android/texture_wrapper.h",
"android/video_frame_factory.h", "android/video_frame_factory.h",
"android/video_frame_factory_impl.cc", "android/video_frame_factory_impl.cc",
"android/video_frame_factory_impl.h", "android/video_frame_factory_impl.h",
...@@ -427,6 +434,8 @@ source_set("android_video_decode_accelerator_unittests") { ...@@ -427,6 +434,8 @@ source_set("android_video_decode_accelerator_unittests") {
"android/media_codec_video_decoder_unittest.cc", "android/media_codec_video_decoder_unittest.cc",
"android/mock_android_video_surface_chooser.cc", "android/mock_android_video_surface_chooser.cc",
"android/mock_android_video_surface_chooser.h", "android/mock_android_video_surface_chooser.h",
"android/mock_command_buffer_stub_wrapper.cc",
"android/mock_command_buffer_stub_wrapper.h",
"android/mock_device_info.cc", "android/mock_device_info.cc",
"android/mock_device_info.h", "android/mock_device_info.h",
"android/mock_promotion_hint_aggregator.cc", "android/mock_promotion_hint_aggregator.cc",
...@@ -436,6 +445,7 @@ source_set("android_video_decode_accelerator_unittests") { ...@@ -436,6 +445,7 @@ source_set("android_video_decode_accelerator_unittests") {
"android/promotion_hint_aggregator_impl_unittest.cc", "android/promotion_hint_aggregator_impl_unittest.cc",
"android/surface_chooser_helper_unittest.cc", "android/surface_chooser_helper_unittest.cc",
"android/surface_texture_gl_owner_unittest.cc", "android/surface_texture_gl_owner_unittest.cc",
"android/texture_pool_unittest.cc",
"android/video_frame_factory_impl_unittest.cc", "android/video_frame_factory_impl_unittest.cc",
] ]
deps = [ deps = [
......
// Copyright 2018 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.
#ifndef MEDIA_GPU_ANDROID_COMMAND_BUFFER_STUB_WRAPPER_H_
#define MEDIA_GPU_ANDROID_COMMAND_BUFFER_STUB_WRAPPER_H_
#include "gpu/ipc/service/command_buffer_stub.h"
namespace media {
// Helpful class to wrap a CommandBufferStub that we can mock out more easily.
// Mocking out a CommandBufferStub + DecoderContext is quite annoying, since we
// really need very little.
// TODO(liberato): consider making this refcounted, so that one injected mock
// can be re-used as its passed from class to class. In that case, it likely
// has to keep its own DestructionObserver list, and register itself as a
// DestructionObserver on the stub.
// TODO(liberato): once this interface is stable, move this to media/gpu and
// use it on non-android platforms.
class CommandBufferStubWrapper {
public:
virtual ~CommandBufferStubWrapper() = default;
// Make the stub's context current. Return true on success.
virtual bool MakeCurrent() = 0;
// Add or remove a destruction observer on the underlying stub.
virtual void AddDestructionObserver(
gpu::CommandBufferStub::DestructionObserver* observer) = 0;
virtual void RemoveDestructionObserver(
gpu::CommandBufferStub::DestructionObserver* observer) = 0;
// To support VideoFrameFactoryImpl, we need at least GetTextureManager().
};
} // namespace media
#endif // MEDIA_GPU_ANDROID_COMMAND_BUFFER_STUB_WRAPPER_H_
// Copyright 2018 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 "command_buffer_stub_wrapper_impl.h"
#include "gpu/ipc/service/command_buffer_stub.h"
namespace media {
CommandBufferStubWrapperImpl::CommandBufferStubWrapperImpl(
gpu::CommandBufferStub* stub)
: stub_(stub) {}
bool CommandBufferStubWrapperImpl::MakeCurrent() {
// Support |!stub_| as a convenience.
return stub_ && stub_->decoder_context()->MakeCurrent();
}
void CommandBufferStubWrapperImpl::AddDestructionObserver(
gpu::CommandBufferStub::DestructionObserver* observer) {
stub_->AddDestructionObserver(observer);
}
void CommandBufferStubWrapperImpl::RemoveDestructionObserver(
gpu::CommandBufferStub::DestructionObserver* observer) {
stub_->RemoveDestructionObserver(observer);
}
} // namespace media
// Copyright 2018 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.
#ifndef MEDIA_GPU_ANDROID_COMMAND_BUFFER_STUB_WRAPPER_IMPL_H_
#define MEDIA_GPU_ANDROID_COMMAND_BUFFER_STUB_WRAPPER_IMPL_H_
#include "media/gpu/android/command_buffer_stub_wrapper.h"
namespace media {
// Implementation that actually talks to a CommandBufferStub
class CommandBufferStubWrapperImpl : public CommandBufferStubWrapper {
public:
explicit CommandBufferStubWrapperImpl(gpu::CommandBufferStub* stub);
~CommandBufferStubWrapperImpl() override = default;
// CommandBufferStubWrapper
bool MakeCurrent() override;
void AddDestructionObserver(
gpu::CommandBufferStub::DestructionObserver* observer) override;
void RemoveDestructionObserver(
gpu::CommandBufferStub::DestructionObserver* observer) override;
private:
gpu::CommandBufferStub* stub_;
};
} // namespace media
#endif // MEDIA_GPU_ANDROID_MEDIA_COMMAND_BUFFER_STUB_WRAPPER_IMPL_H_
// Copyright 2018 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 "media/gpu/android/mock_command_buffer_stub_wrapper.h"
namespace media {
MockCommandBufferStubWrapper::MockCommandBufferStubWrapper() = default;
MockCommandBufferStubWrapper::~MockCommandBufferStubWrapper() = default;
void MockCommandBufferStubWrapper::AddDestructionObserver(
gpu::CommandBufferStub::DestructionObserver* observer) {
ASSERT_FALSE(observer_);
ASSERT_TRUE(observer);
observer_ = observer;
}
void MockCommandBufferStubWrapper::RemoveDestructionObserver(
gpu::CommandBufferStub::DestructionObserver* observer) {
ASSERT_EQ(observer_, observer);
observer_ = nullptr;
}
void MockCommandBufferStubWrapper::NotifyDestruction() {
if (observer_)
observer_->OnWillDestroyStub();
}
} // namespace media
// Copyright 2018 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.
#ifndef MEDIA_GPU_ANDROID_MOCK_COMMAND_BUFFER_STUB_WRAPPER_H_
#define MEDIA_GPU_ANDROID_MOCK_COMMAND_BUFFER_STUB_WRAPPER_H_
#include "media/gpu/android/command_buffer_stub_wrapper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
class MockCommandBufferStubWrapper
: public ::testing::NiceMock<CommandBufferStubWrapper> {
public:
MockCommandBufferStubWrapper();
virtual ~MockCommandBufferStubWrapper();
// CommandBufferStubWrapper
MOCK_METHOD0(MakeCurrent, bool());
void AddDestructionObserver(
gpu::CommandBufferStub::DestructionObserver* observer);
void RemoveDestructionObserver(
gpu::CommandBufferStub::DestructionObserver* observer);
// Notify the observer that we will be destroyed.
void NotifyDestruction();
private:
gpu::CommandBufferStub::DestructionObserver* observer_ = nullptr;
};
} // namespace media
#endif // MEDIA_GPU_ANDROID_MOCK_COMMAND_BUFFER_STUB_WRAPPER_H_
// Copyright 2018 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 "media/gpu/android/texture_pool.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "media/gpu/android/command_buffer_stub_wrapper.h"
#include "media/gpu/android/texture_wrapper.h"
namespace media {
TexturePool::TexturePool(std::unique_ptr<CommandBufferStubWrapper> stub)
: stub_(std::move(stub)) {
if (stub_)
stub_->AddDestructionObserver(this);
}
TexturePool::~TexturePool() {
DestroyAllPlatformTextures();
if (stub_)
stub_->RemoveDestructionObserver(this);
}
void TexturePool::AddTexture(std::unique_ptr<TextureWrapper> texture) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(pool_.find(texture.get()) == pool_.end());
// Don't permit additions after we've lost the stub.
// TODO(liberato): consider making this fail gracefully. However, nobody
// should be doing this, so for now it's a DCHECK.
DCHECK(stub_);
TextureWrapper* texture_raw = texture.get();
pool_[texture_raw] = std::move(texture);
}
void TexturePool::ReleaseTexture(TextureWrapper* texture) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto iter = pool_.find(texture);
DCHECK(iter != pool_.end());
// If we can't make the context current, then notify the texture. Note that
// the wrapper might already have been destroyed, which is fine.
if (iter->second && (!stub_ || !stub_->MakeCurrent()))
texture->ForceContextLost();
pool_.erase(iter);
}
void TexturePool::DestroyAllPlatformTextures() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Don't bother to make the context current if we have no textures.
if (!pool_.size())
return;
// If we can't make the context current, then notify all the textures that
// they can't delete the underlying platform textures.
const bool have_context = stub_ && stub_->MakeCurrent();
// Destroy the wrapper, but keep the entry around in the map. We do this so
// that ReleaseTexture can still check that at least the texture was, at some
// point, in the map. Hopefully, since nobody should be adding textures to
// the pool after we've lost the stub, there's no issue with aliasing if the
// ptr is re-used; it won't be given to us, so it's okay.
for (auto& it : pool_) {
std::unique_ptr<TextureWrapper> texture = std::move(it.second);
if (!texture)
continue;
if (!have_context)
texture->ForceContextLost();
// |texture| will be destroyed.
}
}
void TexturePool::OnWillDestroyStub() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(stub_);
// Since the stub is going away, clean up while we can.
DestroyAllPlatformTextures();
stub_ = nullptr;
}
} // namespace media
// Copyright 2018 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.
#ifndef MEDIA_GPU_ANDROID_TEXTURE_POOL_H_
#define MEDIA_GPU_ANDROID_TEXTURE_POOL_H_
#include <map>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/threading/thread_checker.h"
#include "gpu/ipc/service/command_buffer_stub.h"
#include "media/gpu/media_gpu_export.h"
namespace media {
class CommandBufferStubWrapper;
class TextureWrapper;
// Owns Textures that are used to hold decoded video frames. Allows them to
// outlive the decoder that created them, since decoders are torn down when the
// pipeline is suspended, but decoded frames can be on-screen indefinitely.
// TODO(tmathmeyer): Convert this into a pool. Right now, we just constantly
// add new textures and remove them.
class MEDIA_GPU_EXPORT TexturePool
: public base::RefCounted<TexturePool>,
public gpu::CommandBufferStub::DestructionObserver {
public:
TexturePool(std::unique_ptr<CommandBufferStubWrapper> stub);
// Add a new texture into the pool. This may only be done before |stub_| is
// destroyed. When |stub_| is destroyed, we will destroy any textures that
// are in the pool.
//
// Note that if we were really a pool this would mean "add |texture| into the
// pool of available textures". There would be some other call to allocate
// a texture from the pool.
void AddTexture(std::unique_ptr<TextureWrapper> texture);
// Release a texture back into the pool. |texture| must have been added to
// the pool previously, and not released. Otherwise, this is undefined.
// Note: since we don't actually pool things, this just forgets |texture|.
// It's okay if this is called after we've lost |stub_|.
void ReleaseTexture(TextureWrapper* texture);
protected:
~TexturePool() override;
// DestructionObserver
void OnWillDestroyStub() override;
// When called, we will destroy any platform textures if we have a context,
// or mark them as "lost context" if we don't. This will not actually remove
// entries in |pool_|, but will instead clear the unique_ptr to delete the
// texture. Assuming that nobody adds textures after our stub is destroyed,
// this is still alias-free.
void DestroyAllPlatformTextures();
private:
friend class base::RefCounted<TexturePool>;
THREAD_CHECKER(thread_checker_);
std::unique_ptr<CommandBufferStubWrapper> stub_;
std::map<TextureWrapper*, std::unique_ptr<TextureWrapper>> pool_;
};
} // namespace media
#endif // MEDIA_GPU_ANDROID_TEXTURE_POOL_H_
// Copyright 2018 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 "media/gpu/android/texture_pool.h"
#include <memory>
#include "base/memory/weak_ptr.h"
#include "gpu/command_buffer/service/sequence_id.h"
#include "gpu/ipc/common/gpu_messages.h"
#include "media/gpu/android/mock_command_buffer_stub_wrapper.h"
#include "media/gpu/android/texture_wrapper.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
using testing::NiceMock;
using testing::Return;
// SupportsWeakPtr so it's easy to tell when it has been destroyed.
class MockTextureWrapper : public NiceMock<TextureWrapper>,
public base::SupportsWeakPtr<MockTextureWrapper> {
public:
MockTextureWrapper() {}
~MockTextureWrapper() override {}
MOCK_METHOD0(ForceContextLost, void());
};
class TexturePoolTest : public testing::Test {
public:
TexturePoolTest() = default;
void SetUp() override {
std::unique_ptr<MockCommandBufferStubWrapper> stub =
std::make_unique<MockCommandBufferStubWrapper>();
stub_ = stub.get();
SetContextCanBeCurrent(true);
texture_pool_ = new TexturePool(std::move(stub));
}
using WeakTexture = base::WeakPtr<MockTextureWrapper>;
// Set whether or not |stub_| will report that MakeCurrent worked.
void SetContextCanBeCurrent(bool allow) {
ON_CALL(*stub_, MakeCurrent()).WillByDefault(Return(allow));
}
WeakTexture CreateAndAddTexture() {
std::unique_ptr<MockTextureWrapper> texture =
std::make_unique<MockTextureWrapper>();
WeakTexture texture_weak = texture->AsWeakPtr();
texture_pool_->AddTexture(std::move(texture));
return texture_weak;
}
scoped_refptr<TexturePool> texture_pool_;
MockCommandBufferStubWrapper* stub_ = nullptr;
};
TEST_F(TexturePoolTest, AddAndReleaseTexturesWithContext) {
// Test that adding then deleting a texture destroys it.
WeakTexture texture = CreateAndAddTexture();
// The texture should not be notified that the context was lost.
EXPECT_CALL(*texture.get(), ForceContextLost()).Times(0);
EXPECT_CALL(*stub_, MakeCurrent()).Times(1);
texture_pool_->ReleaseTexture(texture.get());
ASSERT_FALSE(texture);
}
TEST_F(TexturePoolTest, AddAndReleaseTexturesWithoutContext) {
// Test that adding then deleting a texture destroys it, and marks that the
// context is lost.
WeakTexture texture = CreateAndAddTexture();
SetContextCanBeCurrent(false);
EXPECT_CALL(*texture, ForceContextLost()).Times(1);
EXPECT_CALL(*stub_, MakeCurrent()).Times(1);
texture_pool_->ReleaseTexture(texture.get());
ASSERT_FALSE(texture);
}
TEST_F(TexturePoolTest, TexturesAreReleasedOnStubDestructionWithContext) {
// Add multiple textures, and test that they're all destroyed when the stub
// says that it's destroyed.
std::vector<TextureWrapper*> raw_textures;
std::vector<WeakTexture> textures;
for (int i = 0; i < 3; i++) {
textures.push_back(CreateAndAddTexture());
raw_textures.push_back(textures.back().get());
// The context should not be lost.
EXPECT_CALL(*textures.back(), ForceContextLost()).Times(0);
}
EXPECT_CALL(*stub_, MakeCurrent()).Times(1);
stub_->NotifyDestruction();
// TextureWrappers should be destroyed.
for (auto& texture : textures)
ASSERT_FALSE(texture);
// It should be okay to release the textures after they're destroyed, and
// nothing should crash.
for (auto* raw_texture : raw_textures)
texture_pool_->ReleaseTexture(raw_texture);
}
TEST_F(TexturePoolTest, TexturesAreReleasedOnStubDestructionWithoutContext) {
std::vector<TextureWrapper*> raw_textures;
std::vector<WeakTexture> textures;
for (int i = 0; i < 3; i++) {
textures.push_back(CreateAndAddTexture());
raw_textures.push_back(textures.back().get());
EXPECT_CALL(*textures.back(), ForceContextLost()).Times(1);
}
SetContextCanBeCurrent(false);
EXPECT_CALL(*stub_, MakeCurrent()).Times(1);
stub_->NotifyDestruction();
for (auto& texture : textures)
ASSERT_FALSE(texture);
// It should be okay to release the textures after they're destroyed, and
// nothing should crash.
for (auto* raw_texture : raw_textures)
texture_pool_->ReleaseTexture(raw_texture);
}
TEST_F(TexturePoolTest, NonEmptyPoolAfterStubDestructionDoesntCrash) {
// Make sure that we can delete the stub, and verify that pool teardown still
// works (doesn't crash) even though the pool is not empty.
CreateAndAddTexture();
stub_->NotifyDestruction();
}
TEST_F(TexturePoolTest,
NonEmptyPoolAfterStubWithoutContextDestructionDoesntCrash) {
// Make sure that we can delete the stub, and verify that pool teardown still
// works (doesn't crash) even though the pool is not empty.
CreateAndAddTexture();
SetContextCanBeCurrent(false);
stub_->NotifyDestruction();
}
} // namespace media
// Copyright 2018 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 "media/gpu/android/texture_wrapper.h"
#include "gpu/command_buffer/service/texture_manager.h"
namespace media {
TextureWrapperImpl::TextureWrapperImpl(
scoped_refptr<gpu::gles2::TextureRef> texture_ref)
: texture_ref_(std::move(texture_ref)) {}
TextureWrapperImpl::~TextureWrapperImpl() {}
void TextureWrapperImpl::ForceContextLost() {
if (texture_ref_)
texture_ref_->ForceContextLost();
}
} // namespace media
// Copyright 2018 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.
#ifndef MEDIA_GPU_ANDROID_TEXTURE_WRAPPER_H_
#define MEDIA_GPU_ANDROID_TEXTURE_WRAPPER_H_
#include "base/memory/scoped_refptr.h"
namespace gpu {
namespace gles2 {
class TextureRef;
} // namespace gles2
} // namespace gpu
namespace media {
// Temporary class to allow mocking a TextureRef, which has no virtual methods.
// It is expected that this will be replaced by gpu::gles2::AbstractTexture in
// the near future, will will support mocks directly.
class TextureWrapper {
public:
virtual ~TextureWrapper() = default;
virtual void ForceContextLost() = 0;
};
// Since these are temporary classes, the impl might as well go in the same
// file for easier cleanup later.
class TextureWrapperImpl : public TextureWrapper {
public:
TextureWrapperImpl(scoped_refptr<gpu::gles2::TextureRef> texture_ref);
~TextureWrapperImpl() override;
// TextureWrapper
void ForceContextLost() override;
private:
scoped_refptr<gpu::gles2::TextureRef> texture_ref_;
};
} // namespace media
#endif // MEDIA_GPU_ANDROID_TEXTURE_WRAPPER_H_
...@@ -18,9 +18,12 @@ ...@@ -18,9 +18,12 @@
#include "gpu/ipc/service/gpu_channel.h" #include "gpu/ipc/service/gpu_channel.h"
#include "media/base/bind_to_current_loop.h" #include "media/base/bind_to_current_loop.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "media/gpu//android/codec_image.h" #include "media/gpu/android/codec_image.h"
#include "media/gpu//android/codec_image_group.h" #include "media/gpu/android/codec_image_group.h"
#include "media/gpu/android/codec_wrapper.h" #include "media/gpu/android/codec_wrapper.h"
#include "media/gpu/android/command_buffer_stub_wrapper_impl.h"
#include "media/gpu/android/texture_pool.h"
#include "media/gpu/android/texture_wrapper.h"
#include "mojo/public/cpp/bindings/callback_helpers.h" #include "mojo/public/cpp/bindings/callback_helpers.h"
#include "ui/gl/android/surface_texture.h" #include "ui/gl/android/surface_texture.h"
#include "ui/gl/gl_bindings.h" #include "ui/gl/gl_bindings.h"
...@@ -121,7 +124,6 @@ GpuVideoFrameFactory::~GpuVideoFrameFactory() { ...@@ -121,7 +124,6 @@ GpuVideoFrameFactory::~GpuVideoFrameFactory() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (stub_) if (stub_)
stub_->RemoveDestructionObserver(this); stub_->RemoveDestructionObserver(this);
ClearTextureRefs();
} }
scoped_refptr<SurfaceTextureGLOwner> GpuVideoFrameFactory::Initialize( scoped_refptr<SurfaceTextureGLOwner> GpuVideoFrameFactory::Initialize(
...@@ -133,6 +135,10 @@ scoped_refptr<SurfaceTextureGLOwner> GpuVideoFrameFactory::Initialize( ...@@ -133,6 +135,10 @@ scoped_refptr<SurfaceTextureGLOwner> GpuVideoFrameFactory::Initialize(
if (!MakeContextCurrent(stub_)) if (!MakeContextCurrent(stub_))
return nullptr; return nullptr;
stub_->AddDestructionObserver(this); stub_->AddDestructionObserver(this);
texture_pool_ =
new TexturePool(std::make_unique<CommandBufferStubWrapperImpl>(stub_));
decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context()); decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context());
return SurfaceTextureGLOwnerImpl::Create(); return SurfaceTextureGLOwnerImpl::Create();
} }
...@@ -157,16 +163,15 @@ void GpuVideoFrameFactory::CreateVideoFrame( ...@@ -157,16 +163,15 @@ void GpuVideoFrameFactory::CreateVideoFrame(
// Try to render this frame if possible. // Try to render this frame if possible.
internal::MaybeRenderEarly(&images_); internal::MaybeRenderEarly(&images_);
// TODO(sandersd, watk): The VideoFrame release callback will not be called std::unique_ptr<TextureWrapper> texture_wrapper =
// after MojoVideoDecoderService is destructed, so we have to release all std::make_unique<TextureWrapperImpl>(std::move(texture_ref));
// our TextureRefs when |this| is destructed. This can unback outstanding auto drop_texture_ref = base::BindOnce(
// VideoFrames (e.g., the current frame when the player is suspended). The [](scoped_refptr<TexturePool> texture_pool,
// release callback lifetime should be separate from MCVD or TextureWrapper* texture_wrapper, const gpu::SyncToken& sync_token) {
// MojoVideoDecoderService (http://crbug.com/737220). texture_pool->ReleaseTexture(texture_wrapper);
texture_refs_[texture_ref.get()] = texture_ref; },
auto drop_texture_ref = base::BindOnce(&GpuVideoFrameFactory::DropTextureRef, texture_pool_, base::Unretained(texture_wrapper.get()));
weak_factory_.GetWeakPtr(), texture_pool_->AddTexture(std::move(texture_wrapper));
base::Unretained(texture_ref.get()));
// Guarantee that the TextureRef is released even if the VideoFrame is // Guarantee that the TextureRef is released even if the VideoFrame is
// dropped. Otherwise we could keep TextureRefs we don't need alive. // dropped. Otherwise we could keep TextureRefs we don't need alive.
...@@ -271,36 +276,10 @@ void GpuVideoFrameFactory::CreateVideoFrameInternal( ...@@ -271,36 +276,10 @@ void GpuVideoFrameFactory::CreateVideoFrameInternal(
void GpuVideoFrameFactory::OnWillDestroyStub() { void GpuVideoFrameFactory::OnWillDestroyStub() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(stub_); DCHECK(stub_);
ClearTextureRefs();
stub_ = nullptr; stub_ = nullptr;
decoder_helper_ = nullptr; decoder_helper_ = nullptr;
} }
void GpuVideoFrameFactory::ClearTextureRefs() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(stub_ || texture_refs_.empty());
// If we fail to make the context current, we have to notify the TextureRefs
// so they don't try to delete textures without a context.
if (!MakeContextCurrent(stub_)) {
for (const auto& kv : texture_refs_)
kv.first->ForceContextLost();
}
texture_refs_.clear();
}
void GpuVideoFrameFactory::DropTextureRef(gpu::gles2::TextureRef* ref,
const gpu::SyncToken& token) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto it = texture_refs_.find(ref);
if (it == texture_refs_.end())
return;
// If we fail to make the context current, we have to notify the TextureRef
// so it doesn't try to delete a texture without a context.
if (!MakeContextCurrent(stub_))
ref->ForceContextLost();
texture_refs_.erase(it);
}
void GpuVideoFrameFactory::OnImageDestructed(CodecImage* image) { void GpuVideoFrameFactory::OnImageDestructed(CodecImage* image) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
base::Erase(images_, image); base::Erase(images_, image);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
namespace media { namespace media {
class CodecImageGroup; class CodecImageGroup;
class GpuVideoFrameFactory; class GpuVideoFrameFactory;
class TexturePool;
// VideoFrameFactoryImpl creates CodecOutputBuffer backed VideoFrames and tries // VideoFrameFactoryImpl creates CodecOutputBuffer backed VideoFrames and tries
// to eagerly render them to their surface to release the buffers back to the // to eagerly render them to their surface to release the buffers back to the
...@@ -98,25 +99,12 @@ class GpuVideoFrameFactory ...@@ -98,25 +99,12 @@ class GpuVideoFrameFactory
void OnWillDestroyStub() override; void OnWillDestroyStub() override;
// Clears |texture_refs_|. Makes the gl context current.
void ClearTextureRefs();
// Removes |ref| from texture_refs_. Makes the gl context current.
// |token| is ignored because MojoVideoDecoderService guarantees that it has
// already passed by the time we get the callback.
void DropTextureRef(gpu::gles2::TextureRef* ref, const gpu::SyncToken& token);
// Removes |image| from |images_|. // Removes |image| from |images_|.
void OnImageDestructed(CodecImage* image); void OnImageDestructed(CodecImage* image);
// Outstanding images that should be considered for early rendering. // Outstanding images that should be considered for early rendering.
std::vector<CodecImage*> images_; std::vector<CodecImage*> images_;
// Outstanding TextureRefs that are still referenced by a mailbox VideoFrame.
// They're kept alive until their mailboxes are released (or |this| is
// destructed).
std::map<gpu::gles2::TextureRef*, scoped_refptr<gpu::gles2::TextureRef>>
texture_refs_;
gpu::CommandBufferStub* stub_; gpu::CommandBufferStub* stub_;
// Callback to notify us that an image has been destroyed. // Callback to notify us that an image has been destroyed.
...@@ -132,6 +120,9 @@ class GpuVideoFrameFactory ...@@ -132,6 +120,9 @@ class GpuVideoFrameFactory
// replace this when SetImageGroup() is called. // replace this when SetImageGroup() is called.
scoped_refptr<CodecImageGroup> image_group_; scoped_refptr<CodecImageGroup> image_group_;
// Pool which owns all the textures that we create.
scoped_refptr<TexturePool> texture_pool_;
THREAD_CHECKER(thread_checker_); THREAD_CHECKER(thread_checker_);
base::WeakPtrFactory<GpuVideoFrameFactory> weak_factory_; base::WeakPtrFactory<GpuVideoFrameFactory> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(GpuVideoFrameFactory); DISALLOW_COPY_AND_ASSIGN(GpuVideoFrameFactory);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment