// 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 #include #include #include #include #include #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(param.subsampling_x) << "/" << static_cast(param.subsampling_y) << ", border: " << param.border; } class RefCountedBufferReallocTest : public testing::TestWithParam> { 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(buffer_ptr->buffer()->data(kPlaneY)) % 16, 0); EXPECT_EQ( reinterpret_cast(buffer_ptr->buffer()->data(kPlaneU)) % 16, 0); EXPECT_EQ( reinterpret_cast(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(buffer_ptr->buffer()->data(kPlaneY)) % 16, 0); EXPECT_EQ( reinterpret_cast(buffer_ptr->buffer()->data(kPlaneU)) % 16, 0); EXPECT_EQ( reinterpret_cast(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(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(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