blob: abe681ee8e52a88009b8f2ae92d1fa2578561693 [file] [log] [blame]
// Copyright 2021 The libgav1 Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/buffer_pool.h"
#include <climits>
#include <cstdint>
#include <memory>
#include <ostream>
#include <tuple>
#include <utility>
#include "gtest/gtest.h"
#include "src/frame_buffer_utils.h"
#include "src/gav1/decoder_buffer.h"
#include "src/gav1/frame_buffer.h"
#include "src/internal_frame_buffer_list.h"
#include "src/utils/constants.h"
#include "src/utils/types.h"
#include "src/yuv_buffer.h"
namespace libgav1 {
namespace {
TEST(BufferPoolTest, RefCountedBufferPtr) {
InternalFrameBufferList buffer_list;
BufferPool buffer_pool(OnInternalFrameBufferSizeChanged,
GetInternalFrameBuffer, ReleaseInternalFrameBuffer,
&buffer_list);
RefCountedBufferPtr buffer_ptr = buffer_pool.GetFreeBuffer();
EXPECT_NE(buffer_ptr, nullptr);
EXPECT_EQ(buffer_ptr.use_count(), 1);
RefCountedBufferPtr buffer_ptr2 = buffer_ptr;
RefCountedBufferPtr buffer_ptr3 = buffer_ptr;
EXPECT_EQ(buffer_ptr.use_count(), 3);
EXPECT_EQ(buffer_ptr2.use_count(), 3);
EXPECT_EQ(buffer_ptr3.use_count(), 3);
buffer_ptr2 = nullptr;
EXPECT_EQ(buffer_ptr.use_count(), 2);
EXPECT_EQ(buffer_ptr2.use_count(), 0);
EXPECT_EQ(buffer_ptr3.use_count(), 2);
RefCountedBufferPtr buffer_ptr4 = std::move(buffer_ptr);
EXPECT_EQ(buffer_ptr.use_count(), 0);
EXPECT_EQ(buffer_ptr2.use_count(), 0);
EXPECT_EQ(buffer_ptr3.use_count(), 2);
EXPECT_EQ(buffer_ptr4.use_count(), 2);
}
TEST(RefCountedBufferTest, SetFrameDimensions) {
InternalFrameBufferList buffer_list;
BufferPool buffer_pool(OnInternalFrameBufferSizeChanged,
GetInternalFrameBuffer, ReleaseInternalFrameBuffer,
&buffer_list);
RefCountedBufferPtr buffer_ptr = buffer_pool.GetFreeBuffer();
EXPECT_NE(buffer_ptr, nullptr);
// Test the undocumented default values of rows4x4() and columns4x4(). (Not
// sure if this is a good idea.)
EXPECT_EQ(buffer_ptr->rows4x4(), 0);
EXPECT_EQ(buffer_ptr->columns4x4(), 0);
// Test the side effects of SetFrameDimensions().
ObuFrameHeader frame_header = {};
frame_header.rows4x4 = 20;
frame_header.columns4x4 = 30;
EXPECT_TRUE(buffer_ptr->SetFrameDimensions(frame_header));
EXPECT_EQ(buffer_ptr->rows4x4(), 20);
EXPECT_EQ(buffer_ptr->columns4x4(), 30);
}
TEST(RefCountedBuffertTest, WaitUntil) {
InternalFrameBufferList buffer_list;
BufferPool buffer_pool(OnInternalFrameBufferSizeChanged,
GetInternalFrameBuffer, ReleaseInternalFrameBuffer,
&buffer_list);
RefCountedBufferPtr buffer_ptr = buffer_pool.GetFreeBuffer();
EXPECT_NE(buffer_ptr, nullptr);
int progress_row_cache;
buffer_ptr->SetProgress(10);
EXPECT_TRUE(buffer_ptr->WaitUntil(5, &progress_row_cache));
EXPECT_EQ(progress_row_cache, 10);
buffer_ptr->SetFrameState(kFrameStateDecoded);
EXPECT_TRUE(buffer_ptr->WaitUntil(500, &progress_row_cache));
EXPECT_EQ(progress_row_cache, INT_MAX);
buffer_ptr->Abort();
EXPECT_FALSE(buffer_ptr->WaitUntil(50, &progress_row_cache));
}
constexpr struct Params {
int width;
int height;
int8_t subsampling_x;
int8_t subsampling_y;
int border;
} kParams[] = {
{1920, 1080, 1, 1, 96}, //
{1920, 1080, 1, 1, 64}, //
{1920, 1080, 1, 1, 32}, //
{1920, 1080, 1, 1, 160}, //
{1920, 1080, 1, 0, 160}, //
{1920, 1080, 0, 0, 160}, //
};
std::ostream& operator<<(std::ostream& os, const Params& param) {
return os << param.width << "x" << param.height
<< ", subsampling(x/y): " << static_cast<int>(param.subsampling_x)
<< "/" << static_cast<int>(param.subsampling_y)
<< ", border: " << param.border;
}
class RefCountedBufferReallocTest
: public testing::TestWithParam<std::tuple<bool, Params>> {
protected:
const bool use_external_callbacks_ = std::get<0>(GetParam());
const Params& param_ = std::get<1>(GetParam());
};
TEST_P(RefCountedBufferReallocTest, 8Bit) {
InternalFrameBufferList buffer_list;
FrameBufferSizeChangedCallback on_frame_buffer_size_changed = nullptr;
GetFrameBufferCallback get_frame_buffer = nullptr;
ReleaseFrameBufferCallback release_frame_buffer = nullptr;
void* callback_private_data = nullptr;
if (use_external_callbacks_) {
on_frame_buffer_size_changed = OnInternalFrameBufferSizeChanged;
get_frame_buffer = GetInternalFrameBuffer;
release_frame_buffer = ReleaseInternalFrameBuffer;
callback_private_data = &buffer_list;
}
BufferPool buffer_pool(on_frame_buffer_size_changed, get_frame_buffer,
release_frame_buffer, callback_private_data);
RefCountedBufferPtr buffer_ptr = buffer_pool.GetFreeBuffer();
EXPECT_NE(buffer_ptr, nullptr);
const Libgav1ImageFormat image_format = ComposeImageFormat(
/*is_monochrome=*/false, param_.subsampling_x, param_.subsampling_y);
EXPECT_TRUE(buffer_pool.OnFrameBufferSizeChanged(
/*bitdepth=*/8, image_format, param_.width, param_.height, param_.border,
param_.border, param_.border, param_.border));
EXPECT_TRUE(buffer_ptr->Realloc(
/*bitdepth=*/8, /*is_monochrome=*/false, param_.width, param_.height,
param_.subsampling_x, param_.subsampling_y, param_.border, param_.border,
param_.border, param_.border));
// The first row of each plane is aligned at 16-byte boundaries.
EXPECT_EQ(
reinterpret_cast<uintptr_t>(buffer_ptr->buffer()->data(kPlaneY)) % 16, 0);
EXPECT_EQ(
reinterpret_cast<uintptr_t>(buffer_ptr->buffer()->data(kPlaneU)) % 16, 0);
EXPECT_EQ(
reinterpret_cast<uintptr_t>(buffer_ptr->buffer()->data(kPlaneV)) % 16, 0);
// Subsequent rows are aligned at 16-byte boundaries.
EXPECT_EQ(buffer_ptr->buffer()->stride(kPlaneY) % 16, 0);
EXPECT_EQ(buffer_ptr->buffer()->stride(kPlaneU) % 16, 0);
EXPECT_EQ(buffer_ptr->buffer()->stride(kPlaneV) % 16, 0);
// Check the borders.
EXPECT_EQ(buffer_ptr->buffer()->left_border(kPlaneY), param_.border);
EXPECT_EQ(buffer_ptr->buffer()->right_border(kPlaneY), param_.border);
EXPECT_EQ(buffer_ptr->buffer()->top_border(kPlaneY), param_.border);
EXPECT_EQ(buffer_ptr->buffer()->bottom_border(kPlaneY), param_.border);
EXPECT_EQ(buffer_ptr->buffer()->left_border(kPlaneU),
param_.border >> param_.subsampling_x);
EXPECT_EQ(buffer_ptr->buffer()->right_border(kPlaneU),
param_.border >> param_.subsampling_x);
EXPECT_EQ(buffer_ptr->buffer()->top_border(kPlaneU),
param_.border >> param_.subsampling_y);
EXPECT_EQ(buffer_ptr->buffer()->bottom_border(kPlaneU),
param_.border >> param_.subsampling_y);
EXPECT_EQ(buffer_ptr->buffer()->left_border(kPlaneV),
param_.border >> param_.subsampling_x);
EXPECT_EQ(buffer_ptr->buffer()->right_border(kPlaneV),
param_.border >> param_.subsampling_x);
EXPECT_EQ(buffer_ptr->buffer()->top_border(kPlaneV),
param_.border >> param_.subsampling_y);
EXPECT_EQ(buffer_ptr->buffer()->bottom_border(kPlaneV),
param_.border >> param_.subsampling_y);
// Write to the upper-left corner of the border.
uint8_t* y_buffer = buffer_ptr->buffer()->data(kPlaneY);
int y_stride = buffer_ptr->buffer()->stride(kPlaneY);
y_buffer[-buffer_ptr->buffer()->left_border(kPlaneY) -
buffer_ptr->buffer()->top_border(kPlaneY) * y_stride] = 0;
// Write to the lower-right corner of the border.
uint8_t* v_buffer = buffer_ptr->buffer()->data(kPlaneV);
int v_stride = buffer_ptr->buffer()->stride(kPlaneV);
v_buffer[(buffer_ptr->buffer()->height(kPlaneV) +
buffer_ptr->buffer()->bottom_border(kPlaneV) - 1) *
v_stride +
buffer_ptr->buffer()->width(kPlaneV) +
buffer_ptr->buffer()->right_border(kPlaneV) - 1] = 0;
}
#if LIBGAV1_MAX_BITDEPTH >= 10
TEST_P(RefCountedBufferReallocTest, 10Bit) {
InternalFrameBufferList buffer_list;
FrameBufferSizeChangedCallback on_frame_buffer_size_changed = nullptr;
GetFrameBufferCallback get_frame_buffer = nullptr;
ReleaseFrameBufferCallback release_frame_buffer = nullptr;
void* callback_private_data = nullptr;
if (use_external_callbacks_) {
on_frame_buffer_size_changed = OnInternalFrameBufferSizeChanged;
get_frame_buffer = GetInternalFrameBuffer;
release_frame_buffer = ReleaseInternalFrameBuffer;
callback_private_data = &buffer_list;
}
BufferPool buffer_pool(on_frame_buffer_size_changed, get_frame_buffer,
release_frame_buffer, callback_private_data);
RefCountedBufferPtr buffer_ptr = buffer_pool.GetFreeBuffer();
EXPECT_NE(buffer_ptr, nullptr);
const Libgav1ImageFormat image_format = ComposeImageFormat(
/*is_monochrome=*/false, param_.subsampling_x, param_.subsampling_y);
EXPECT_TRUE(buffer_pool.OnFrameBufferSizeChanged(
/*bitdepth=*/8, image_format, param_.width, param_.height, param_.border,
param_.border, param_.border, param_.border));
EXPECT_TRUE(buffer_ptr->Realloc(
/*bitdepth=*/10, /*is_monochrome=*/false, param_.width, param_.height,
param_.subsampling_x, param_.subsampling_y, param_.border, param_.border,
param_.border, param_.border));
// The first row of each plane is aligned at 16-byte boundaries.
EXPECT_EQ(
reinterpret_cast<uintptr_t>(buffer_ptr->buffer()->data(kPlaneY)) % 16, 0);
EXPECT_EQ(
reinterpret_cast<uintptr_t>(buffer_ptr->buffer()->data(kPlaneU)) % 16, 0);
EXPECT_EQ(
reinterpret_cast<uintptr_t>(buffer_ptr->buffer()->data(kPlaneV)) % 16, 0);
// Subsequent rows are aligned at 16-byte boundaries.
EXPECT_EQ(buffer_ptr->buffer()->stride(kPlaneY) % 16, 0);
EXPECT_EQ(buffer_ptr->buffer()->stride(kPlaneU) % 16, 0);
EXPECT_EQ(buffer_ptr->buffer()->stride(kPlaneV) % 16, 0);
// Check the borders.
EXPECT_EQ(buffer_ptr->buffer()->left_border(kPlaneY), param_.border);
EXPECT_EQ(buffer_ptr->buffer()->right_border(kPlaneY), param_.border);
EXPECT_EQ(buffer_ptr->buffer()->top_border(kPlaneY), param_.border);
EXPECT_EQ(buffer_ptr->buffer()->bottom_border(kPlaneY), param_.border);
EXPECT_EQ(buffer_ptr->buffer()->left_border(kPlaneU),
param_.border >> param_.subsampling_x);
EXPECT_EQ(buffer_ptr->buffer()->right_border(kPlaneU),
param_.border >> param_.subsampling_x);
EXPECT_EQ(buffer_ptr->buffer()->top_border(kPlaneU),
param_.border >> param_.subsampling_y);
EXPECT_EQ(buffer_ptr->buffer()->bottom_border(kPlaneU),
param_.border >> param_.subsampling_y);
EXPECT_EQ(buffer_ptr->buffer()->left_border(kPlaneV),
param_.border >> param_.subsampling_x);
EXPECT_EQ(buffer_ptr->buffer()->right_border(kPlaneV),
param_.border >> param_.subsampling_x);
EXPECT_EQ(buffer_ptr->buffer()->top_border(kPlaneV),
param_.border >> param_.subsampling_y);
EXPECT_EQ(buffer_ptr->buffer()->bottom_border(kPlaneV),
param_.border >> param_.subsampling_y);
// Write to the upper-left corner of the border.
auto* y_buffer =
reinterpret_cast<uint16_t*>(buffer_ptr->buffer()->data(kPlaneY));
int y_stride = buffer_ptr->buffer()->stride(kPlaneY) / sizeof(uint16_t);
y_buffer[-buffer_ptr->buffer()->left_border(kPlaneY) -
buffer_ptr->buffer()->top_border(kPlaneY) * y_stride] = 0;
// Write to the lower-right corner of the border.
auto* v_buffer =
reinterpret_cast<uint16_t*>(buffer_ptr->buffer()->data(kPlaneV));
int v_stride = buffer_ptr->buffer()->stride(kPlaneV) / sizeof(uint16_t);
v_buffer[(buffer_ptr->buffer()->height(kPlaneV) +
buffer_ptr->buffer()->bottom_border(kPlaneV) - 1) *
v_stride +
buffer_ptr->buffer()->width(kPlaneV) +
buffer_ptr->buffer()->right_border(kPlaneV) - 1] = 0;
}
#endif // LIBGAV1_MAX_BITDEPTH >= 10
INSTANTIATE_TEST_SUITE_P(
Default, RefCountedBufferReallocTest,
testing::Combine(testing::Bool(), // use_external_callbacks
testing::ValuesIn(kParams)));
} // namespace
} // namespace libgav1