| // 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 |