blob: 711598960c8464988b25f298a7d004b8d32ea196 [file] [log] [blame]
// Copyright 2019 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/pooled_shared_image_video_provider.h"
#include <list>
#include "base/memory/ptr_util.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "gpu/ipc/common/command_buffer_id.h"
#include "media/gpu/android/mock_shared_image_video_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::Mock;
namespace media {
class PooledSharedImageVideoProviderTest : public testing::Test {
public:
class MockGpuHelper : public PooledSharedImageVideoProvider::GpuHelper {
public:
MockGpuHelper(gpu::SyncToken* sync_token_out)
: sync_token_out_(sync_token_out) {}
// PooledSharedImageVideoProvider::GpuHelper
void OnImageReturned(const gpu::SyncToken& sync_token,
scoped_refptr<CodecImageHolder> codec_image_holder,
base::OnceClosure cb,
scoped_refptr<gpu::RefCountedLock> lock) override {
*sync_token_out_ = sync_token;
// Run the output cb.
std::move(cb).Run();
}
private:
gpu::SyncToken* sync_token_out_ = nullptr;
};
PooledSharedImageVideoProviderTest() = default;
void SetUp() override {
task_runner_ = base::SequencedTaskRunnerHandle::Get();
base::SequenceBound<MockGpuHelper> mock_gpu_helper(task_runner_,
&sync_token_);
std::unique_ptr<MockSharedImageVideoProvider> mock_provider =
std::make_unique<MockSharedImageVideoProvider>();
mock_provider_raw_ = mock_provider.get();
provider_ = base::WrapUnique(new PooledSharedImageVideoProvider(
std::move(mock_gpu_helper), std::move(mock_provider),
/*lock=*/nullptr));
}
// Return an ImageReadyCB that saves the ImageRecord in |image_records_|.
SharedImageVideoProvider::ImageReadyCB SaveImageRecordCB() {
return base::BindOnce(
[](std::list<SharedImageVideoProvider::ImageRecord>* output_list,
SharedImageVideoProvider::ImageRecord record) {
output_list->push_back(std::move(record));
},
&image_records_);
}
// Request an image from |provier_|, which we expect will call through to
// |mock_provider_raw_|. Have |mock_provider_raw_| return an image, too.
void RequestAndProvideImage(const SharedImageVideoProvider::ImageSpec& spec) {
EXPECT_CALL(*mock_provider_raw_, MockRequestImage()).Times(1);
provider_->RequestImage(SaveImageRecordCB(), spec);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClearExpectations(mock_provider_raw_);
mock_provider_raw_->ProvideOneRequestedImage();
}
base::test::TaskEnvironment task_environment_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
// Provider under test.
std::unique_ptr<PooledSharedImageVideoProvider> provider_;
MockSharedImageVideoProvider* mock_provider_raw_ = nullptr;
gpu::SyncToken sync_token_;
scoped_refptr<gpu::TextureOwner> texture_owner_;
// Image records that we've received from |provider|, via SaveImageRecordCB().
std::list<SharedImageVideoProvider::ImageRecord> image_records_;
};
TEST_F(PooledSharedImageVideoProviderTest, InitializeForwardsGpuCallback) {
bool was_called = false;
auto gpu_init_cb = base::BindOnce(
[](bool* flag, scoped_refptr<gpu::SharedContextState>) { *flag = true; },
&was_called);
provider_->Initialize(std::move(gpu_init_cb));
std::move(mock_provider_raw_->gpu_init_cb_).Run(nullptr);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
}
TEST_F(PooledSharedImageVideoProviderTest, RequestImageRequestsMultipleImages) {
// Test the RequestImage will keep requesting images from the underlying
// provider as long as we don't return any.
SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
RequestAndProvideImage(spec);
RequestAndProvideImage(spec);
RequestAndProvideImage(spec);
EXPECT_EQ(image_records_.size(), 3u);
}
TEST_F(PooledSharedImageVideoProviderTest, ReleasingAnImageForwardsSyncToken) {
// Calling the release callback on an image should forward the sync token to
// our gpu helper.
SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
RequestAndProvideImage(spec);
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferIdFromChannelAndRoute(2, 3), 4);
std::move(image_records_.back().release_cb).Run(sync_token);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(sync_token, sync_token_);
}
TEST_F(PooledSharedImageVideoProviderTest,
ReleasingAnImageDoesntRunUnderlyingReleaseCallback) {
// Verify that releasing an image doesn't call the underlying release callback
// on it. Presumably, it should be sent back to the pool instead.
SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
RequestAndProvideImage(spec);
// Release the image.
image_records_.pop_back();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 0);
}
TEST_F(PooledSharedImageVideoProviderTest, RequestImageReusesReturnedImages) {
// Test the RequestImage will return images without requesting new ones, if
// some have been returned to the pool.
SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
// Request two images.
RequestAndProvideImage(spec);
RequestAndProvideImage(spec);
EXPECT_EQ(image_records_.size(), 2u);
// Now return one, and request another.
image_records_.pop_back();
// Let the release CB run.
base::RunLoop().RunUntilIdle();
// Shouldn't call MockRequestImage a third time.
EXPECT_CALL(*mock_provider_raw_, MockRequestImage()).Times(0);
provider_->RequestImage(SaveImageRecordCB(), spec);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(image_records_.size(), 2u);
}
TEST_F(PooledSharedImageVideoProviderTest,
DeletingProviderWithOutstandingImagesDoesntCrash) {
// Destroying |provider_| with outstanding images shouldn't break anything.
SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
provider_->RequestImage(SaveImageRecordCB(), spec);
base::RunLoop().RunUntilIdle();
provider_.reset();
base::RunLoop().RunUntilIdle();
// Shouldn't crash.
}
TEST_F(PooledSharedImageVideoProviderTest,
ReturnedImagesAreReleasedAfterSpecChange) {
// When we change the ImageSpec, old images should be released on the
// underlying provider as they are returned.
SharedImageVideoProvider::ImageSpec spec_1(gfx::Size(1, 1), 0u);
SharedImageVideoProvider::ImageSpec spec_2(gfx::Size(1, 2), 0u);
RequestAndProvideImage(spec_1);
RequestAndProvideImage(spec_2);
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 0);
// Return image 1, and it should run the underlying release callback since it
// doesn't match the pool spec.
image_records_.pop_front();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
// Returning image 2 should not, since it should be put into the pool.
image_records_.pop_front();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
}
TEST_F(PooledSharedImageVideoProviderTest, SizeChangeEmptiesPool) {
// Verify that a size change in the ImageSpec causes the pool to be emptied.
SharedImageVideoProvider::ImageSpec spec_1(gfx::Size(1, 1), 0u);
SharedImageVideoProvider::ImageSpec spec_2(gfx::Size(1, 2), 0u);
// Request an image with |spec_1| and release it, to send it to the pool.
RequestAndProvideImage(spec_1);
image_records_.pop_front();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 0);
// Request an image with |spec_2|, which should release the first one.
RequestAndProvideImage(spec_2);
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
}
TEST_F(PooledSharedImageVideoProviderTest, GenerationIdChangeEmptiesPool) {
// Verify that a change in the generation id causes the pool to be emptied.
SharedImageVideoProvider::ImageSpec spec_1(gfx::Size(1, 1), 0u);
SharedImageVideoProvider::ImageSpec spec_2(gfx::Size(1, 1), 1u);
RequestAndProvideImage(spec_1);
image_records_.pop_front();
base::RunLoop().RunUntilIdle();
RequestAndProvideImage(spec_2);
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
}
TEST_F(PooledSharedImageVideoProviderTest, InFlightSpecChangeProvidesImage) {
// If we change the ImageSpec between requesting and receiving an image from
// the provider, it should still provide the image to the requestor.
SharedImageVideoProvider::ImageSpec spec_1(gfx::Size(1, 1), 0u);
SharedImageVideoProvider::ImageSpec spec_2(gfx::Size(1, 1), 1u);
// Request both images before providing either.
EXPECT_CALL(*mock_provider_raw_, MockRequestImage()).Times(2);
provider_->RequestImage(SaveImageRecordCB(), spec_1);
provider_->RequestImage(SaveImageRecordCB(), spec_2);
base::RunLoop().RunUntilIdle();
// Provide the |spec_1| image. Nothing should be released since it should
// fulfill the first request.
mock_provider_raw_->ProvideOneRequestedImage();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 0);
EXPECT_EQ(image_records_.size(), 1u);
// Provide the |spec_2| image, which should also be provided to us.
mock_provider_raw_->ProvideOneRequestedImage();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 0);
EXPECT_EQ(image_records_.size(), 2u);
// Drop the |spec_1| image, which should be released rather than added back to
// the pool.
image_records_.pop_front();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
// Drop the |spec_2| image, which should be pooled rather than released.
image_records_.pop_front();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(mock_provider_raw_->num_release_callbacks_, 1);
}
} // namespace media