blob: 01fe16772b0a1a8cb5ab27755b1a69e2fed57a15 [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 <memory>
#include <utility>
#include <vector>
#include "base/stl_util.h"
#include "base/test/task_environment.h"
#include "cc/paint/paint_image.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "gpu/ipc/client/image_decode_accelerator_proxy.h"
#include "gpu/ipc/common/command_buffer_id.h"
#include "gpu/ipc/common/gpu_messages.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/color_space.h"
using ::testing::DeleteArg;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::StrictMock;
namespace gpu {
namespace {
constexpr int kChannelId = 5;
constexpr int32_t kRasterCmdBufferRouteId = 3;
constexpr gfx::Size kOutputSize(2, 2);
MATCHER_P(IpcMessageEqualTo, expected, "") {
// Get params from actual IPC message.
GpuChannelMsg_ScheduleImageDecode::Param actual_param_tuple;
if (!GpuChannelMsg_ScheduleImageDecode::Read(arg, &actual_param_tuple))
return false;
GpuChannelMsg_ScheduleImageDecode_Params params =
std::get<0>(actual_param_tuple);
const uint64_t release_count = std::get<1>(actual_param_tuple);
// Get params from expected IPC Message.
GpuChannelMsg_ScheduleImageDecode::Param expected_param_tuple;
if (!GpuChannelMsg_ScheduleImageDecode::Read(expected, &expected_param_tuple))
return false;
GpuChannelMsg_ScheduleImageDecode_Params expected_params =
std::get<0>(expected_param_tuple);
const uint64_t expected_release_count = std::get<1>(expected_param_tuple);
// Compare all relevant fields.
return arg->routing_id() == expected->routing_id() &&
release_count == expected_release_count &&
params.encoded_data == expected_params.encoded_data &&
params.output_size == expected_params.output_size &&
params.raster_decoder_route_id ==
expected_params.raster_decoder_route_id &&
params.transfer_cache_entry_id ==
expected_params.transfer_cache_entry_id &&
params.discardable_handle_shm_id ==
expected_params.discardable_handle_shm_id &&
params.discardable_handle_shm_offset ==
expected_params.discardable_handle_shm_offset &&
params.discardable_handle_release_count ==
expected_params.discardable_handle_release_count &&
params.target_color_space == expected_params.target_color_space &&
params.needs_mips == expected_params.needs_mips;
}
} // namespace
class MockGpuChannelHost : public GpuChannelHost {
public:
MockGpuChannelHost() : MockGpuChannelHost(GPUInfo()) {}
MockGpuChannelHost(const GPUInfo& info)
: GpuChannelHost(kChannelId,
info,
GpuFeatureInfo(),
mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(
mojo::kInvalidHandleValue))) {}
MOCK_METHOD1(Send, bool(IPC::Message*));
protected:
~MockGpuChannelHost() override {}
};
class ImageDecodeAcceleratorProxyTest : public ::testing::Test {
public:
ImageDecodeAcceleratorProxyTest()
: gpu_channel_host_(
base::MakeRefCounted<StrictMock<MockGpuChannelHost>>()),
proxy_(gpu_channel_host_.get(),
static_cast<int32_t>(
GpuChannelReservedRoutes::kImageDecodeAccelerator)) {}
~ImageDecodeAcceleratorProxyTest() override = default;
protected:
base::test::SingleThreadTaskEnvironment task_environment_;
scoped_refptr<StrictMock<MockGpuChannelHost>> gpu_channel_host_;
ImageDecodeAcceleratorProxy proxy_;
};
TEST_F(ImageDecodeAcceleratorProxyTest, ScheduleImageDecodeSendsMessage) {
const uint8_t image[4] = {1, 2, 3, 4};
base::span<const uint8_t> encoded_data =
base::span<const uint8_t>(image, base::size(image));
const gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
GpuChannelMsg_ScheduleImageDecode_Params expected_params;
expected_params.encoded_data.assign(encoded_data.begin(), encoded_data.end());
expected_params.output_size = kOutputSize;
expected_params.raster_decoder_route_id = kRasterCmdBufferRouteId;
expected_params.transfer_cache_entry_id = 1u;
expected_params.discardable_handle_shm_id = 2;
expected_params.discardable_handle_shm_offset = 3u;
expected_params.discardable_handle_release_count = 4u;
expected_params.target_color_space = color_space;
expected_params.needs_mips = false;
GpuChannelMsg_ScheduleImageDecode expected_message(
static_cast<int32_t>(GpuChannelReservedRoutes::kImageDecodeAccelerator),
std::move(expected_params), /*release_count=*/1u);
{
EXPECT_CALL(*gpu_channel_host_, Send(IpcMessageEqualTo(&expected_message)))
.Times(1)
.WillOnce(DoAll(DeleteArg<0>(),
Return(false))); // Delete object passed to Send.
}
SyncToken token = proxy_.ScheduleImageDecode(
encoded_data, kOutputSize,
CommandBufferIdFromChannelAndRoute(kChannelId, kRasterCmdBufferRouteId),
/*transfer_cache_entry_id=*/1u,
/*discardable_handle_shm_id=*/2,
/*discardable_handle_shm_offset=*/3u,
/*discardable_handle_release_count=*/4u, color_space,
/*needs_mips=*/false);
task_environment_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(gpu_channel_host_.get());
EXPECT_EQ(ChannelIdFromCommandBufferId(token.command_buffer_id()),
kChannelId);
EXPECT_EQ(
RouteIdFromCommandBufferId(token.command_buffer_id()),
static_cast<int32_t>(GpuChannelReservedRoutes::kImageDecodeAccelerator));
EXPECT_EQ(token.release_count(), 1u);
}
class ImageDecodeAcceleratorProxySubsamplingTest
: public testing::TestWithParam<cc::YUVSubsampling> {
public:
ImageDecodeAcceleratorProxySubsamplingTest() = default;
~ImageDecodeAcceleratorProxySubsamplingTest() override = default;
protected:
base::test::SingleThreadTaskEnvironment task_environment_;
};
TEST_P(ImageDecodeAcceleratorProxySubsamplingTest, JPEGSubsamplingIsSupported) {
cc::ImageHeaderMetadata image_metadata;
image_metadata.yuv_subsampling = GetParam();
image_metadata.image_type = cc::ImageType::kJPEG;
image_metadata.all_data_received_prior_to_decode = true;
image_metadata.has_embedded_color_profile = false;
image_metadata.image_size = gfx::Size(100, 200);
image_metadata.jpeg_is_progressive = false;
ImageDecodeAcceleratorSupportedProfile profile;
profile.image_type = ImageDecodeAcceleratorType::kJpeg;
profile.min_encoded_dimensions = gfx::Size(0, 0);
profile.max_encoded_dimensions = gfx::Size(1920, 1080);
static_assert(
// TODO(andrescj): refactor to instead have a static_assert at the
// declaration site of ImageDecodeAcceleratorSubsampling to make sure it
// has the same number of entries as cc::YUVSubsampling.
static_cast<int>(ImageDecodeAcceleratorSubsampling::kMaxValue) == 2,
"ImageDecodeAcceleratorProxySubsamplingTest.JPEGSubsamplingIsSupported "
"must be adapted to support all subsampling factors in "
"ImageDecodeAcceleratorSubsampling");
switch (GetParam()) {
case cc::YUVSubsampling::k420:
profile.subsamplings.push_back(ImageDecodeAcceleratorSubsampling::k420);
break;
case cc::YUVSubsampling::k422:
profile.subsamplings.push_back(ImageDecodeAcceleratorSubsampling::k422);
break;
case cc::YUVSubsampling::k444:
profile.subsamplings.push_back(ImageDecodeAcceleratorSubsampling::k444);
break;
default:
return;
}
GPUInfo gpu_info;
gpu_info.image_decode_accelerator_supported_profiles.push_back(profile);
auto gpu_channel_host(
base::MakeRefCounted<StrictMock<MockGpuChannelHost>>(gpu_info));
ImageDecodeAcceleratorProxy proxy(
gpu_channel_host.get(),
static_cast<int32_t>(GpuChannelReservedRoutes::kImageDecodeAccelerator));
EXPECT_TRUE(proxy.IsImageSupported(&image_metadata));
}
TEST_P(ImageDecodeAcceleratorProxySubsamplingTest,
JPEGSubsamplingIsNotSupported) {
cc::ImageHeaderMetadata image_metadata;
image_metadata.yuv_subsampling = GetParam();
image_metadata.image_type = cc::ImageType::kJPEG;
image_metadata.all_data_received_prior_to_decode = true;
image_metadata.has_embedded_color_profile = false;
image_metadata.image_size = gfx::Size(100, 200);
image_metadata.jpeg_is_progressive = false;
ImageDecodeAcceleratorSupportedProfile profile;
profile.image_type = ImageDecodeAcceleratorType::kJpeg;
profile.min_encoded_dimensions = gfx::Size(0, 0);
profile.max_encoded_dimensions = gfx::Size(1920, 1080);
static_assert(
// TODO(andrescj): refactor to instead have a static_assert at the
// declaration site of ImageDecodeAcceleratorSubsampling to make sure it
// has the same number of entries as cc::YUVSubsampling.
static_cast<int>(ImageDecodeAcceleratorSubsampling::kMaxValue) == 2,
"ImageDecodeAcceleratorProxySubsamplingTest.JPEGSubsamplingIsNotSupported"
"must be adapted to support all subsampling factors in "
"ImageDecodeAcceleratorSubsampling");
// Advertise support for all subsamplings except the GetParam() one.
if (GetParam() != cc::YUVSubsampling::k420)
profile.subsamplings.push_back(ImageDecodeAcceleratorSubsampling::k420);
if (GetParam() != cc::YUVSubsampling::k422)
profile.subsamplings.push_back(ImageDecodeAcceleratorSubsampling::k422);
if (GetParam() != cc::YUVSubsampling::k444)
profile.subsamplings.push_back(ImageDecodeAcceleratorSubsampling::k444);
GPUInfo gpu_info;
gpu_info.image_decode_accelerator_supported_profiles.push_back(profile);
auto gpu_channel_host(
base::MakeRefCounted<StrictMock<MockGpuChannelHost>>(gpu_info));
ImageDecodeAcceleratorProxy proxy(
gpu_channel_host.get(),
static_cast<int32_t>(GpuChannelReservedRoutes::kImageDecodeAccelerator));
EXPECT_FALSE(proxy.IsImageSupported(&image_metadata));
}
INSTANTIATE_TEST_SUITE_P(ImageDecodeAcceleratorProxySubsample,
ImageDecodeAcceleratorProxySubsamplingTest,
testing::Values(cc::YUVSubsampling::k410,
cc::YUVSubsampling::k411,
cc::YUVSubsampling::k420,
cc::YUVSubsampling::k422,
cc::YUVSubsampling::k440,
cc::YUVSubsampling::k444,
cc::YUVSubsampling::kUnknown));
} // namespace gpu