Commit 7e7f86b3 authored by Vasiliy Telezhnikov's avatar Vasiliy Telezhnikov Committed by Commit Bot

Fix ordering of GetFrameInfo requests

This CL adds requests queue to FrameInfoHelperImpl to make sure all
requests are ordered to keep VideoFrameFactoryImpl pipeline ordered.

Before this change VFFI would cache FrameInfo to avoid thread hops to
GPU thread and it was possible that some CreateVideoFrame requests will
be completed out-of-order.

After this change VFFI doesn't cache FrameInfo and always call
FrameInfoHelper. FrameInfoHelper does all necessary caching to avoid
unnecessary hops to GPU thread and guarantees that callbacks with
FrameInfo will come in order.

Change-Id: Iacb63cf3746991c911f1f0d5e3667db3dbd56c68
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2304092
Commit-Queue: Vasiliy Telezhnikov <vasilyt@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#790127}
parent ee99ff7f
...@@ -4,10 +4,13 @@ ...@@ -4,10 +4,13 @@
#include "media/gpu/android/frame_info_helper.h" #include "media/gpu/android/frame_info_helper.h"
#include "base/threading/sequence_bound.h"
#include "gpu/command_buffer/service/shared_image_video.h" #include "gpu/command_buffer/service/shared_image_video.h"
#include "gpu/ipc/service/command_buffer_stub.h" #include "gpu/ipc/service/command_buffer_stub.h"
#include "gpu/ipc/service/gpu_channel.h" #include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h" #include "gpu/ipc/service/gpu_channel_manager.h"
#include "media/base/bind_to_current_loop.h"
#include "media/gpu/android/codec_output_buffer_renderer.h"
namespace media { namespace media {
...@@ -21,70 +24,71 @@ FrameInfoHelper::FrameInfo& FrameInfoHelper::FrameInfo::operator=( ...@@ -21,70 +24,71 @@ FrameInfoHelper::FrameInfo& FrameInfoHelper::FrameInfo::operator=(
// Concrete implementation of FrameInfoHelper that renders output buffers and // Concrete implementation of FrameInfoHelper that renders output buffers and
// gets the FrameInfo they need. // gets the FrameInfo they need.
class FrameInfoHelperImpl : public FrameInfoHelper, class FrameInfoHelperImpl : public FrameInfoHelper {
public gpu::CommandBufferStub::DestructionObserver {
public: public:
FrameInfoHelperImpl(SharedImageVideoProvider::GetStubCB get_stub_cb) { FrameInfoHelperImpl(scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
SharedImageVideoProvider::GetStubCB get_stub_cb) {
on_gpu_ = base::SequenceBound<OnGpu>(std::move(gpu_task_runner),
std::move(get_stub_cb));
}
~FrameInfoHelperImpl() override = default;
void GetFrameInfo(std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
FrameInfoReadyCB callback) override {
Request request = {.buffer_renderer = std::move(buffer_renderer),
.callback = std::move(callback)};
requests_.push(std::move(request));
// If there were no pending requests start processing queue now.
if (requests_.size() == 1)
ProcessRequestsQueue();
}
private:
struct Request {
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer;
FrameInfoReadyCB callback;
};
class OnGpu : public gpu::CommandBufferStub::DestructionObserver {
public:
OnGpu(SharedImageVideoProvider::GetStubCB get_stub_cb) {
stub_ = get_stub_cb.Run(); stub_ = get_stub_cb.Run();
if (stub_) if (stub_)
stub_->AddDestructionObserver(this); stub_->AddDestructionObserver(this);
} }
FrameInfoHelperImpl() = default; ~OnGpu() override {
~FrameInfoHelperImpl() override {
if (stub_) if (stub_)
stub_->RemoveDestructionObserver(this); stub_->RemoveDestructionObserver(this);
} }
void OnWillDestroyStub(bool have_context) override {
DCHECK(stub_);
stub_ = nullptr;
}
void GetFrameInfo( void GetFrameInfo(
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer, std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
base::OnceCallback< base::OnceCallback<void(std::unique_ptr<CodecOutputBufferRenderer>,
void(std::unique_ptr<CodecOutputBufferRenderer>, FrameInfo, bool)> cb) base::Optional<FrameInfo>)> cb) {
override { DCHECK(buffer_renderer);
if (!buffer_renderer) {
std::move(cb).Run(nullptr, FrameInfo(), false);
return;
}
auto texture_owner = buffer_renderer->texture_owner(); auto texture_owner = buffer_renderer->texture_owner();
DCHECK(texture_owner);
FrameInfo info; base::Optional<FrameInfo> info;
// Indicates that the FrameInfo is reliable and can be cached by caller. if (buffer_renderer->RenderToTextureOwnerFrontBuffer(
// It's true if we either return cached values or we attempted to render CodecOutputBufferRenderer::BindingsMode::kDontRestoreIfBound)) {
// frame and succeeded. info.emplace();
bool success = true;
// We default to visible size if if we can't get real size
info.coded_size = buffer_renderer->size();
info.visible_rect = gfx::Rect(info.coded_size);
if (texture_owner) {
if (visible_size_ == buffer_renderer->size()) {
info = frame_info_;
} else if (buffer_renderer->RenderToTextureOwnerFrontBuffer(
CodecOutputBufferRenderer::BindingsMode::
kDontRestoreIfBound)) {
visible_size_ = buffer_renderer->size();
texture_owner->GetCodedSizeAndVisibleRect( texture_owner->GetCodedSizeAndVisibleRect(
visible_size_, &frame_info_.coded_size, &frame_info_.visible_rect); buffer_renderer->size(), &info->coded_size, &info->visible_rect);
frame_info_.ycbcr_info = GetYCbCrInfo(texture_owner.get()); info->ycbcr_info = GetYCbCrInfo(texture_owner.get());
info = frame_info_;
} else {
// We attempted to render frame and failed, mark request as failed so
// caller won't cache best-guess values.
success = false;
}
} }
std::move(cb).Run(std::move(buffer_renderer), info, success); std::move(cb).Run(std::move(buffer_renderer), info);
}
void OnWillDestroyStub(bool have_context) override {
DCHECK(stub_);
stub_ = nullptr;
} }
private: private:
...@@ -92,35 +96,112 @@ class FrameInfoHelperImpl : public FrameInfoHelper, ...@@ -92,35 +96,112 @@ class FrameInfoHelperImpl : public FrameInfoHelper,
base::Optional<gpu::VulkanYCbCrInfo> GetYCbCrInfo( base::Optional<gpu::VulkanYCbCrInfo> GetYCbCrInfo(
gpu::TextureOwner* texture_owner) { gpu::TextureOwner* texture_owner) {
gpu::ContextResult result; gpu::ContextResult result;
if (!stub_) if (!stub_)
return base::nullopt; return base::nullopt;
auto shared_context = auto shared_context =
stub_->channel()->gpu_channel_manager()->GetSharedContextState(&result); stub_->channel()->gpu_channel_manager()->GetSharedContextState(
&result);
auto context_provider = auto context_provider =
(result == gpu::ContextResult::kSuccess) ? shared_context : nullptr; (result == gpu::ContextResult::kSuccess) ? shared_context : nullptr;
if (!context_provider) if (!context_provider)
return base::nullopt; return base::nullopt;
return gpu::SharedImageVideo::GetYcbcrInfo(texture_owner, context_provider); return gpu::SharedImageVideo::GetYcbcrInfo(texture_owner,
context_provider);
} }
gpu::CommandBufferStub* stub_ = nullptr; gpu::CommandBufferStub* stub_ = nullptr;
};
FrameInfo GetFrameInfoWithVisibleSize(const gfx::Size& visible_size) {
FrameInfo info;
info.coded_size = visible_size;
info.visible_rect = gfx::Rect(visible_size);
return info;
}
void OnFrameInfoReady(
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
base::Optional<FrameInfo> frame_info) {
DCHECK(buffer_renderer);
DCHECK(!requests_.empty());
auto& request = requests_.front();
if (frame_info) {
visible_size_ = buffer_renderer->size();
frame_info_ = *frame_info;
std::move(request.callback).Run(std::move(buffer_renderer), frame_info_);
} else {
// It's possible that we will fail to render frame and so weren't able to
// obtain FrameInfo. In this case we don't cache new values and complete
// current request with visible size, we will attempt to render next frame
// with next request.
auto info = GetFrameInfoWithVisibleSize(buffer_renderer->size());
std::move(request.callback)
.Run(std::move(buffer_renderer), std::move(info));
}
requests_.pop();
ProcessRequestsQueue();
}
void ProcessRequestsQueue() {
while (!requests_.empty()) {
auto& request = requests_.front();
if (!request.buffer_renderer) {
// If we don't have buffer_renderer we can Run callback immediately.
std::move(request.callback).Run(nullptr, FrameInfo());
} else if (!request.buffer_renderer->texture_owner()) {
// If there is no texture_owner (SurfaceView case), we can't render
// frame and get proper size. But as Display Compositor won't render
// this frame the actual size is not important, assume coded_size =
// visible_size.
auto info =
GetFrameInfoWithVisibleSize(request.buffer_renderer->size());
std::move(request.callback)
.Run(std::move(request.buffer_renderer), std::move(info));
} else if (visible_size_ == request.buffer_renderer->size()) {
// We have cached the results of last frame info request with the same
// size. We assume that coded_size doesn't change if the visible_size
// stays the same.
std::move(request.callback)
.Run(std::move(request.buffer_renderer), frame_info_);
} else {
// We have texture_owner and we don't have cached value, so we need to
// hop to GPU thread and render the frame to get proper size.
auto cb = BindToCurrentLoop(
base::BindOnce(&FrameInfoHelperImpl::OnFrameInfoReady,
weak_factory_.GetWeakPtr()));
on_gpu_.Post(FROM_HERE, &OnGpu::GetFrameInfo,
std::move(request.buffer_renderer), std::move(cb));
// We didn't complete this request quite yet, so we can't process queue
// any further.
break;
}
requests_.pop();
}
}
base::SequenceBound<OnGpu> on_gpu_;
std::queue<Request> requests_;
// Cached values.
FrameInfo frame_info_; FrameInfo frame_info_;
gfx::Size visible_size_; gfx::Size visible_size_;
base::WeakPtrFactory<FrameInfoHelperImpl> weak_factory_{this};
}; };
// static // static
base::SequenceBound<FrameInfoHelper> FrameInfoHelper::Create( std::unique_ptr<FrameInfoHelper> FrameInfoHelper::Create(
scoped_refptr<base::SequencedTaskRunner> gpu_task_runner, scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
SharedImageVideoProvider::GetStubCB get_stub_cb) { SharedImageVideoProvider::GetStubCB get_stub_cb) {
return base::SequenceBound<FrameInfoHelperImpl>(std::move(gpu_task_runner), return std::make_unique<FrameInfoHelperImpl>(std::move(gpu_task_runner),
std::move(get_stub_cb)); std::move(get_stub_cb));
} }
std::unique_ptr<FrameInfoHelper> FrameInfoHelper::CreateForTesting() {
return std::make_unique<FrameInfoHelperImpl>();
}
} // namespace media } // namespace media
...@@ -6,12 +6,11 @@ ...@@ -6,12 +6,11 @@
#define MEDIA_GPU_ANDROID_FRAME_INFO_HELPER_H_ #define MEDIA_GPU_ANDROID_FRAME_INFO_HELPER_H_
#include "base/optional.h" #include "base/optional.h"
#include "base/threading/sequence_bound.h"
#include "media/gpu/android/codec_image.h"
#include "media/gpu/android/shared_image_video_provider.h" #include "media/gpu/android/shared_image_video_provider.h"
#include "media/gpu/media_gpu_export.h" #include "media/gpu/media_gpu_export.h"
namespace media { namespace media {
class CodecOutputBufferRenderer;
// Helper class to fetch YCbCrInfo for Vulkan from a CodecImage. // Helper class to fetch YCbCrInfo for Vulkan from a CodecImage.
class MEDIA_GPU_EXPORT FrameInfoHelper { class MEDIA_GPU_EXPORT FrameInfoHelper {
...@@ -29,12 +28,14 @@ class MEDIA_GPU_EXPORT FrameInfoHelper { ...@@ -29,12 +28,14 @@ class MEDIA_GPU_EXPORT FrameInfoHelper {
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info; base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info;
}; };
static base::SequenceBound<FrameInfoHelper> Create( using FrameInfoReadyCB =
base::OnceCallback<void(std::unique_ptr<CodecOutputBufferRenderer>,
FrameInfo)>;
static std::unique_ptr<FrameInfoHelper> Create(
scoped_refptr<base::SequencedTaskRunner> gpu_task_runner, scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
SharedImageVideoProvider::GetStubCB get_stub_cb); SharedImageVideoProvider::GetStubCB get_stub_cb);
static std::unique_ptr<FrameInfoHelper> CreateForTesting();
virtual ~FrameInfoHelper() = default; virtual ~FrameInfoHelper() = default;
// Call |cb| with the FrameInfo. Will render |buffer_renderer| to the front // Call |cb| with the FrameInfo. Will render |buffer_renderer| to the front
...@@ -42,9 +43,11 @@ class MEDIA_GPU_EXPORT FrameInfoHelper { ...@@ -42,9 +43,11 @@ class MEDIA_GPU_EXPORT FrameInfoHelper {
// attempt to get YCbCrInfo and cache it. If all necessary info is cached the // attempt to get YCbCrInfo and cache it. If all necessary info is cached the
// call will leave buffer_renderer intact and it can be rendered later. // call will leave buffer_renderer intact and it can be rendered later.
// Rendering can fail for reasons. This function will make best efforts to // Rendering can fail for reasons. This function will make best efforts to
// fill FrameInfo which can be used to create VideoFrame, but shouldn't be // fill FrameInfo which can be used to create VideoFrame.
// cached by caller. Last parameter in |cb| is bool that indicates that info //
// is reliable. // Callbacks will be executed and on callers sequence and guaranteed to be
// called in order of GetFrameInfo calls. Callback can be called before this
// function returns if all necessary info is available right away.
// //
// While this API might seem to be out of its Vulkan mind, it's this // While this API might seem to be out of its Vulkan mind, it's this
// complicated to (a) prevent rendering frames out of order to the front // complicated to (a) prevent rendering frames out of order to the front
...@@ -52,9 +55,7 @@ class MEDIA_GPU_EXPORT FrameInfoHelper { ...@@ -52,9 +55,7 @@ class MEDIA_GPU_EXPORT FrameInfoHelper {
// can't get a YCbCrInfo from a CodecImage due to timeouts. // can't get a YCbCrInfo from a CodecImage due to timeouts.
virtual void GetFrameInfo( virtual void GetFrameInfo(
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer, std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
base::OnceCallback<void(std::unique_ptr<CodecOutputBufferRenderer>, FrameInfoReadyCB callback) = 0;
FrameInfo,
bool)> cb) = 0;
protected: protected:
FrameInfoHelper() = default; FrameInfoHelper() = default;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "gpu/command_buffer/service/mock_texture_owner.h" #include "gpu/command_buffer/service/mock_texture_owner.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -18,11 +19,19 @@ namespace { ...@@ -18,11 +19,19 @@ namespace {
constexpr gfx::Size kTestVisibleSize(100, 100); constexpr gfx::Size kTestVisibleSize(100, 100);
constexpr gfx::Size kTestVisibleSize2(110, 110); constexpr gfx::Size kTestVisibleSize2(110, 110);
constexpr gfx::Size kTestCodedSize(128, 128); constexpr gfx::Size kTestCodedSize(128, 128);
std::unique_ptr<FrameInfoHelper> CreateHelper() {
auto task_runner = base::ThreadTaskRunnerHandle::Get();
auto get_stub_cb =
base::Bind([]() -> gpu::CommandBufferStub* { return nullptr; });
return FrameInfoHelper::Create(std::move(task_runner),
std::move(get_stub_cb));
}
} // namespace } // namespace
class FrameInfoHelperTest : public testing::Test { class FrameInfoHelperTest : public testing::Test {
public: public:
FrameInfoHelperTest() : helper_(FrameInfoHelper::CreateForTesting()) {} FrameInfoHelperTest() : helper_(CreateHelper()) {}
protected: protected:
void GetFrameInfo( void GetFrameInfo(
...@@ -31,13 +40,13 @@ class FrameInfoHelperTest : public testing::Test { ...@@ -31,13 +40,13 @@ class FrameInfoHelperTest : public testing::Test {
bool called = false; bool called = false;
auto callback = base::BindLambdaForTesting( auto callback = base::BindLambdaForTesting(
[&](std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer, [&](std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
FrameInfoHelper::FrameInfo info, bool success) { FrameInfoHelper::FrameInfo info) {
ASSERT_EQ(buffer_renderer_raw, buffer_renderer.get()); ASSERT_EQ(buffer_renderer_raw, buffer_renderer.get());
called = true; called = true;
last_get_frame_info_succeeded_ = success;
last_frame_info_ = info; last_frame_info_ = info;
}); });
helper_->GetFrameInfo(std::move(buffer_renderer), callback); helper_->GetFrameInfo(std::move(buffer_renderer), callback);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called); ASSERT_TRUE(called);
} }
...@@ -67,15 +76,12 @@ class FrameInfoHelperTest : public testing::Test { ...@@ -67,15 +76,12 @@ class FrameInfoHelperTest : public testing::Test {
base::test::SingleThreadTaskEnvironment task_environment_; base::test::SingleThreadTaskEnvironment task_environment_;
std::unique_ptr<FrameInfoHelper> helper_; std::unique_ptr<FrameInfoHelper> helper_;
bool last_get_frame_info_succeeded_ = false;
FrameInfoHelper::FrameInfo last_frame_info_; FrameInfoHelper::FrameInfo last_frame_info_;
}; };
TEST_F(FrameInfoHelperTest, NoBufferRenderer) { TEST_F(FrameInfoHelperTest, NoBufferRenderer) {
// If there is no buffer renderer we shouldn't crash and report that request // If there is no buffer renderer we shouldn't crash.
// failed.
GetFrameInfo(nullptr); GetFrameInfo(nullptr);
EXPECT_FALSE(last_get_frame_info_succeeded_);
} }
TEST_F(FrameInfoHelperTest, TextureOwner) { TEST_F(FrameInfoHelperTest, TextureOwner) {
...@@ -93,7 +99,6 @@ TEST_F(FrameInfoHelperTest, TextureOwner) { ...@@ -93,7 +99,6 @@ TEST_F(FrameInfoHelperTest, TextureOwner) {
// as failed. // as failed.
EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(0); EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(0);
GetFrameInfo(std::move(buffer1)); GetFrameInfo(std::move(buffer1));
EXPECT_FALSE(last_get_frame_info_succeeded_);
EXPECT_EQ(last_frame_info_.coded_size, kTestVisibleSize); EXPECT_EQ(last_frame_info_.coded_size, kTestVisibleSize);
Mock::VerifyAndClearExpectations(texture_owner.get()); Mock::VerifyAndClearExpectations(texture_owner.get());
...@@ -101,7 +106,6 @@ TEST_F(FrameInfoHelperTest, TextureOwner) { ...@@ -101,7 +106,6 @@ TEST_F(FrameInfoHelperTest, TextureOwner) {
// be called and result should be kTestCodedSize instead of kTestVisibleSize. // be called and result should be kTestCodedSize instead of kTestVisibleSize.
EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(1); EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(1);
GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, texture_owner)); GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, texture_owner));
EXPECT_TRUE(last_get_frame_info_succeeded_);
EXPECT_EQ(last_frame_info_.coded_size, kTestCodedSize); EXPECT_EQ(last_frame_info_.coded_size, kTestCodedSize);
Mock::VerifyAndClearExpectations(texture_owner.get()); Mock::VerifyAndClearExpectations(texture_owner.get());
...@@ -109,22 +113,102 @@ TEST_F(FrameInfoHelperTest, TextureOwner) { ...@@ -109,22 +113,102 @@ TEST_F(FrameInfoHelperTest, TextureOwner) {
// size. GetCodedSizeAndVisibleRect should not be called. // size. GetCodedSizeAndVisibleRect should not be called.
EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(0); EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(0);
GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, texture_owner)); GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, texture_owner));
EXPECT_TRUE(last_get_frame_info_succeeded_);
EXPECT_EQ(last_frame_info_.coded_size, kTestCodedSize); EXPECT_EQ(last_frame_info_.coded_size, kTestCodedSize);
Mock::VerifyAndClearExpectations(texture_owner.get()); Mock::VerifyAndClearExpectations(texture_owner.get());
// Verify that we render if the visible size changed. // Verify that we render if the visible size changed.
EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(1); EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(1);
GetFrameInfo(CreateBufferRenderer(kTestVisibleSize2, texture_owner)); GetFrameInfo(CreateBufferRenderer(kTestVisibleSize2, texture_owner));
EXPECT_TRUE(last_get_frame_info_succeeded_);
EXPECT_EQ(last_frame_info_.coded_size, kTestCodedSize); EXPECT_EQ(last_frame_info_.coded_size, kTestCodedSize);
} }
TEST_F(FrameInfoHelperTest, Overlay) { TEST_F(FrameInfoHelperTest, Overlay) {
// In overlay case we always use visible size. // In overlay case we always use visible size.
GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, nullptr)); GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, nullptr));
EXPECT_TRUE(last_get_frame_info_succeeded_);
EXPECT_EQ(last_frame_info_.coded_size, kTestVisibleSize); EXPECT_EQ(last_frame_info_.coded_size, kTestVisibleSize);
} }
TEST_F(FrameInfoHelperTest, SwitchBetweenOverlayAndTextureOwner) {
auto texture_owner = base::MakeRefCounted<NiceMock<gpu::MockTextureOwner>>(
0, nullptr, nullptr, true);
// Return CodedSize when GetCodedSizeAndVisibleRect is called.
ON_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _))
.WillByDefault(SetArgPointee<1>(kTestCodedSize));
// In overlay case we always use visible size.
GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, nullptr));
EXPECT_EQ(last_frame_info_.coded_size, kTestVisibleSize);
// Verify that when we switch to TextureOwner we request new size.
EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(1);
GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, texture_owner));
EXPECT_EQ(last_frame_info_.coded_size, kTestCodedSize);
Mock::VerifyAndClearExpectations(texture_owner.get());
// Switch back to overlay and verify that we falled back to visible size and
// didn't try to render image.
EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(0);
GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, nullptr));
EXPECT_EQ(last_frame_info_.coded_size, kTestVisibleSize);
Mock::VerifyAndClearExpectations(texture_owner.get());
// Verify that when we switch to TextureOwner we use cached size.
EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(0);
GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, texture_owner));
EXPECT_EQ(last_frame_info_.coded_size, kTestCodedSize);
Mock::VerifyAndClearExpectations(texture_owner.get());
// Switch back to overlay again.
EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(0);
GetFrameInfo(CreateBufferRenderer(kTestVisibleSize, nullptr));
EXPECT_EQ(last_frame_info_.coded_size, kTestVisibleSize);
Mock::VerifyAndClearExpectations(texture_owner.get());
// Verify that when we switch to TextureOwner with resize we render another
// frame.
EXPECT_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _)).Times(1);
GetFrameInfo(CreateBufferRenderer(kTestVisibleSize2, texture_owner));
EXPECT_EQ(last_frame_info_.coded_size, kTestCodedSize);
Mock::VerifyAndClearExpectations(texture_owner.get());
}
TEST_F(FrameInfoHelperTest, OrderingTest) {
auto texture_owner = base::MakeRefCounted<NiceMock<gpu::MockTextureOwner>>(
0, nullptr, nullptr, true);
// Return CodedSize when GetCodedSizeAndVisibleRect is called.
ON_CALL(*texture_owner, GetCodedSizeAndVisibleRect(_, _, _))
.WillByDefault(SetArgPointee<1>(kTestCodedSize));
auto buffer_renderer = CreateBufferRenderer(kTestVisibleSize, texture_owner);
// Create first callback and request frame.
const auto* buffer_renderer_raw = buffer_renderer.get();
bool called = false;
auto callback = base::BindLambdaForTesting(
[&](std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
FrameInfoHelper::FrameInfo info) {
ASSERT_EQ(buffer_renderer_raw, buffer_renderer.get());
called = true;
last_frame_info_ = info;
});
helper_->GetFrameInfo(std::move(buffer_renderer), callback);
// Create run after callback.
bool run_after_called = false;
auto run_after_callback = base::BindLambdaForTesting(
[&](std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
FrameInfoHelper::FrameInfo info) {
ASSERT_EQ(buffer_renderer.get(), nullptr);
// Expect first callback was called before this.
EXPECT_TRUE(called);
run_after_called = true;
});
helper_->GetFrameInfo(nullptr, run_after_callback);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
ASSERT_TRUE(run_after_called);
}
} // namespace media } // namespace media
...@@ -81,7 +81,7 @@ VideoFrameFactoryImpl::VideoFrameFactoryImpl( ...@@ -81,7 +81,7 @@ VideoFrameFactoryImpl::VideoFrameFactoryImpl(
const gpu::GpuPreferences& gpu_preferences, const gpu::GpuPreferences& gpu_preferences,
std::unique_ptr<SharedImageVideoProvider> image_provider, std::unique_ptr<SharedImageVideoProvider> image_provider,
std::unique_ptr<MaybeRenderEarlyManager> mre_manager, std::unique_ptr<MaybeRenderEarlyManager> mre_manager,
base::SequenceBound<FrameInfoHelper> frame_info_helper) std::unique_ptr<FrameInfoHelper> frame_info_helper)
: image_provider_(std::move(image_provider)), : image_provider_(std::move(image_provider)),
gpu_task_runner_(std::move(gpu_task_runner)), gpu_task_runner_(std::move(gpu_task_runner)),
enable_threaded_texture_mailboxes_( enable_threaded_texture_mailboxes_(
...@@ -113,10 +113,6 @@ void VideoFrameFactoryImpl::SetSurfaceBundle( ...@@ -113,10 +113,6 @@ void VideoFrameFactoryImpl::SetSurfaceBundle(
// changing the TextureOwner. This is temporary. See ImageSpec. // changing the TextureOwner. This is temporary. See ImageSpec.
image_spec_.generation_id++; image_spec_.generation_id++;
// Reset cached visible size as we might switched between overlay and texture
// owner mode.
visible_size_ = gfx::Size();
if (!surface_bundle) { if (!surface_bundle) {
// Clear everything, just so we're not holding a reference. // Clear everything, just so we're not holding a reference.
codec_buffer_wait_coordinator_ = nullptr; codec_buffer_wait_coordinator_ = nullptr;
...@@ -185,48 +181,20 @@ void VideoFrameFactoryImpl::CreateVideoFrame( ...@@ -185,48 +181,20 @@ void VideoFrameFactoryImpl::CreateVideoFrame(
void VideoFrameFactoryImpl::RequestImage( void VideoFrameFactoryImpl::RequestImage(
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer, std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
ImageWithInfoReadyCB image_ready_cb) { ImageWithInfoReadyCB image_ready_cb) {
if (buffer_renderer && visible_size_ == buffer_renderer->size()) { auto info_cb =
auto cb = base::BindOnce(std::move(image_ready_cb),
std::move(buffer_renderer), frame_info_);
image_provider_->RequestImage(
std::move(cb), image_spec_,
codec_buffer_wait_coordinator_
? codec_buffer_wait_coordinator_->texture_owner()
: nullptr);
return;
}
// We need to reset size to make sure VFFI pipeline is still ordered.
// e.g: CreateVideoFrame is called with new size. We post task to GPU thread
// to get new frame info. While we wait CreateVideoFrame might be called with
// old size again and if we don't reset size here we will skip GPU hop and new
// frame will be created earlier than first one.
visible_size_ = gfx::Size();
auto info_cb = BindToCurrentLoop(
base::BindOnce(&VideoFrameFactoryImpl::CreateVideoFrame_OnFrameInfoReady, base::BindOnce(&VideoFrameFactoryImpl::CreateVideoFrame_OnFrameInfoReady,
weak_factory_.GetWeakPtr(), std::move(image_ready_cb), weak_factory_.GetWeakPtr(), std::move(image_ready_cb),
codec_buffer_wait_coordinator_)); codec_buffer_wait_coordinator_);
frame_info_helper_.Post(FROM_HERE, &FrameInfoHelper::GetFrameInfo, frame_info_helper_->GetFrameInfo(std::move(buffer_renderer),
std::move(buffer_renderer), std::move(info_cb)); std::move(info_cb));
} }
void VideoFrameFactoryImpl::CreateVideoFrame_OnFrameInfoReady( void VideoFrameFactoryImpl::CreateVideoFrame_OnFrameInfoReady(
ImageWithInfoReadyCB image_ready_cb, ImageWithInfoReadyCB image_ready_cb,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator, scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer, std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
FrameInfoHelper::FrameInfo frame_info, FrameInfoHelper::FrameInfo frame_info) {
bool success) {
// To get frame info we need to render frame which might fail for variety of
// reason. FrameInfoHelper will provide best values we can proceed with, but
// we should not cache it and attempt to get info for next frame.
if (success) {
frame_info_ = frame_info;
visible_size_ = output_buffer_renderer->size();
}
// If we don't have output buffer here we can't rely on reply from // If we don't have output buffer here we can't rely on reply from
// FrameInfoHelper as there might be not cached value and we can't render // FrameInfoHelper as there might be not cached value and we can't render
// nothing. But in this case call comes from RunAfterPendingVideoFrames and we // nothing. But in this case call comes from RunAfterPendingVideoFrames and we
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "gpu/config/gpu_preferences.h" #include "gpu/config/gpu_preferences.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "media/gpu/android/codec_buffer_wait_coordinator.h" #include "media/gpu/android/codec_buffer_wait_coordinator.h"
...@@ -52,7 +51,7 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory { ...@@ -52,7 +51,7 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
const gpu::GpuPreferences& gpu_preferences, const gpu::GpuPreferences& gpu_preferences,
std::unique_ptr<SharedImageVideoProvider> image_provider, std::unique_ptr<SharedImageVideoProvider> image_provider,
std::unique_ptr<MaybeRenderEarlyManager> mre_manager, std::unique_ptr<MaybeRenderEarlyManager> mre_manager,
base::SequenceBound<FrameInfoHelper> frame_info_helper); std::unique_ptr<FrameInfoHelper> frame_info_helper);
~VideoFrameFactoryImpl() override; ~VideoFrameFactoryImpl() override;
void Initialize(OverlayMode overlay_mode, InitCB init_cb) override; void Initialize(OverlayMode overlay_mode, InitCB init_cb) override;
...@@ -105,8 +104,7 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory { ...@@ -105,8 +104,7 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
ImageWithInfoReadyCB image_ready_cb, ImageWithInfoReadyCB image_ready_cb,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator, scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer, std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
FrameInfoHelper::FrameInfo frame_info, FrameInfoHelper::FrameInfo frame_info);
bool success);
MaybeRenderEarlyManager* mre_manager() const { return mre_manager_.get(); } MaybeRenderEarlyManager* mre_manager() const { return mre_manager_.get(); }
...@@ -128,12 +126,8 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory { ...@@ -128,12 +126,8 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
std::unique_ptr<MaybeRenderEarlyManager> mre_manager_; std::unique_ptr<MaybeRenderEarlyManager> mre_manager_;
// Caches FrameInfo and visible size it was cached for. // Helper to get coded_size and optional Vulkan YCbCrInfo.
gfx::Size visible_size_; std::unique_ptr<FrameInfoHelper> frame_info_helper_;
FrameInfoHelper::FrameInfo frame_info_;
// Optional helper to get the Vulkan YCbCrInfo.
base::SequenceBound<FrameInfoHelper> frame_info_helper_;
// The current image spec that we'll use to request images. // The current image spec that we'll use to request images.
SharedImageVideoProvider::ImageSpec image_spec_; SharedImageVideoProvider::ImageSpec image_spec_;
......
...@@ -44,46 +44,14 @@ class MockMaybeRenderEarlyManager : public MaybeRenderEarlyManager { ...@@ -44,46 +44,14 @@ class MockMaybeRenderEarlyManager : public MaybeRenderEarlyManager {
class MockFrameInfoHelper : public FrameInfoHelper, class MockFrameInfoHelper : public FrameInfoHelper,
public DestructionObservable { public DestructionObservable {
public: public:
MockFrameInfoHelper(MockFrameInfoHelper** thiz) { *thiz = this; } void GetFrameInfo(std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
FrameInfoReadyCB cb) override {
void GetFrameInfo(
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
base::OnceCallback<
void(std::unique_ptr<CodecOutputBufferRenderer>, FrameInfo, bool)> cb)
override {
MockGetFrameInfo(buffer_renderer.get());
cb_ = std::move(cb);
buffer_renderer_ = std::move(buffer_renderer);
if (run_callback_automatically_) {
RunWithYcbCrInfo(true);
base::RunLoop().RunUntilIdle();
}
}
void RunWithYcbCrInfo(bool success) {
DCHECK(buffer_renderer_);
FrameInfo info; FrameInfo info;
info.coded_size = buffer_renderer_->size(); info.coded_size = buffer_renderer->size();
info.visible_rect = gfx::Rect(info.coded_size); info.visible_rect = gfx::Rect(info.coded_size);
std::move(cb_).Run(std::move(buffer_renderer_), info, success); std::move(cb).Run(std::move(buffer_renderer), info);
} }
void set_run_callback_automatically(bool run_callback_automatically) {
run_callback_automatically_ = run_callback_automatically;
}
MOCK_METHOD1(MockGetFrameInfo,
void(CodecOutputBufferRenderer* buffer_renderer));
private:
bool run_callback_automatically_ = true;
base::OnceCallback<
void(std::unique_ptr<CodecOutputBufferRenderer>, FrameInfo, bool)>
cb_;
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer_;
}; };
class VideoFrameFactoryImplTest : public testing::Test { class VideoFrameFactoryImplTest : public testing::Test {
...@@ -96,15 +64,11 @@ class VideoFrameFactoryImplTest : public testing::Test { ...@@ -96,15 +64,11 @@ class VideoFrameFactoryImplTest : public testing::Test {
auto mre_manager = std::make_unique<MockMaybeRenderEarlyManager>(); auto mre_manager = std::make_unique<MockMaybeRenderEarlyManager>();
mre_manager_raw_ = mre_manager.get(); mre_manager_raw_ = mre_manager.get();
auto ycbcr_helper = base::SequenceBound<MockFrameInfoHelper>( auto info_helper = std::make_unique<MockFrameInfoHelper>();
task_runner_, &ycbcr_helper_raw_);
base::RunLoop().RunUntilIdle(); // Init |ycbcr_helper_raw_|.
ycbcr_destruction_observer_ =
ycbcr_helper_raw_->CreateDestructionObserver();
impl_ = std::make_unique<VideoFrameFactoryImpl>( impl_ = std::make_unique<VideoFrameFactoryImpl>(
task_runner_, gpu_preferences_, std::move(image_provider), task_runner_, gpu_preferences_, std::move(image_provider),
std::move(mre_manager), std::move(ycbcr_helper)); std::move(mre_manager), std::move(info_helper));
auto texture_owner = base::MakeRefCounted<NiceMock<gpu::MockTextureOwner>>( auto texture_owner = base::MakeRefCounted<NiceMock<gpu::MockTextureOwner>>(
0, nullptr, nullptr, true); 0, nullptr, nullptr, true);
auto codec_buffer_wait_coordinator = auto codec_buffer_wait_coordinator =
...@@ -177,7 +141,6 @@ class VideoFrameFactoryImplTest : public testing::Test { ...@@ -177,7 +141,6 @@ class VideoFrameFactoryImplTest : public testing::Test {
// Sent to |impl_| by RequestVideoFrame.. // Sent to |impl_| by RequestVideoFrame..
base::MockCallback<VideoFrameFactory::OnceOutputCB> output_cb_; base::MockCallback<VideoFrameFactory::OnceOutputCB> output_cb_;
MockFrameInfoHelper* ycbcr_helper_raw_ = nullptr;
std::unique_ptr<DestructionObserver> ycbcr_destruction_observer_; std::unique_ptr<DestructionObserver> ycbcr_destruction_observer_;
gpu::GpuPreferences gpu_preferences_; gpu::GpuPreferences gpu_preferences_;
...@@ -272,75 +235,4 @@ TEST_F(VideoFrameFactoryImplTest, ...@@ -272,75 +235,4 @@ TEST_F(VideoFrameFactoryImplTest,
impl_ = nullptr; impl_ = nullptr;
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
TEST_F(VideoFrameFactoryImplTest, DoesCallFrameInfoHelperIfVulkan) {
// We will be driving callback by ourselves in this test.
ycbcr_helper_raw_->set_run_callback_automatically(false);
// Expect call to get info for the first frame.
EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(1);
RequestVideoFrame();
// Provide info. It should send image request.
ycbcr_helper_raw_->RunWithYcbCrInfo(true);
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(ycbcr_helper_raw_);
// Fulfilling image request should provide video frame.
EXPECT_CALL(output_cb_, Run(_)).Times(1);
auto image_record = MakeImageRecord();
image_provider_raw_->ProvideOneRequestedImage(&image_record);
base::RunLoop().RunUntilIdle();
// Verify that no more calls happen, since we don't want thread hops on every
// frame. Note that multiple could be dispatched before now. It should still
// send along a VideoFrame, though.
EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(0);
EXPECT_CALL(output_cb_, Run(_)).Times(1);
RequestVideoFrame();
auto other_image_record = MakeImageRecord();
// If the helper hasn't been destroyed, then we don't expect it to be called.
image_provider_raw_->ProvideOneRequestedImage(&other_image_record);
base::RunLoop().RunUntilIdle();
}
TEST_F(VideoFrameFactoryImplTest, NullYCbCrInfoDoesntCrash) {
// We will be driving callback by ourselves in this test.
ycbcr_helper_raw_->set_run_callback_automatically(false);
// Expect call to get info for the first frame.
EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(1);
RequestVideoFrame();
// Provide info. It should send image request.
ycbcr_helper_raw_->RunWithYcbCrInfo(false);
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(ycbcr_helper_raw_);
// Fulfilling image request should provide video frame.
EXPECT_CALL(output_cb_, Run(_)).Times(1);
auto image_record = MakeImageRecord();
image_provider_raw_->ProvideOneRequestedImage(&image_record);
base::RunLoop().RunUntilIdle();
// Verify that we will get call to GetFrameInfo as previous one failed.
EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(1);
EXPECT_CALL(output_cb_, Run(_)).Times(1);
RequestVideoFrame();
ycbcr_helper_raw_->RunWithYcbCrInfo(true);
base::RunLoop().RunUntilIdle();
auto other_image_record = MakeImageRecord();
// If the helper hasn't been destroyed, then we don't expect it to be called.
image_provider_raw_->ProvideOneRequestedImage(&other_image_record);
base::RunLoop().RunUntilIdle();
}
} // namespace media } // namespace media
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