1 // Copyright 2019 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/residual_buffer_pool.h"
16
17 #include <memory>
18 #include <mutex> // NOLINT (unapproved c++11 header)
19 #include <utility>
20
21 namespace libgav1 {
22 namespace {
23
24 // The maximum queue size is derived using the following formula:
25 // ((sb_size * sb_size) / 16) + (2 * (((sb_size / x) * (sb_size / y)) / 16)).
26 // Where:
27 // sb_size is the superblock size (64 or 128).
28 // 16 is 4*4 which is kMinTransformWidth * kMinTransformHeight.
29 // x is subsampling_x + 1.
30 // y is subsampling_y + 1.
31 // The first component is for the Y plane and the second component is for the U
32 // and V planes.
33 // For example, for 128x128 superblocks with 422 subsampling the size is:
34 // ((128 * 128) / 16) + (2 * (((128 / 2) * (128 / 1)) / 16)) = 2048.
35 //
36 // First dimension: use_128x128_superblock.
37 // Second dimension: subsampling_x.
38 // Third dimension: subsampling_y.
39 constexpr int kMaxQueueSize[2][2][2] = {
40 // 64x64 superblocks.
41 {
42 {768, 512},
43 {512, 384},
44 },
45 // 128x128 superblocks.
46 {
47 {3072, 2048},
48 {2048, 1536},
49 },
50 };
51
52 } // namespace
53
~ResidualBufferStack()54 ResidualBufferStack::~ResidualBufferStack() {
55 while (top_ != nullptr) {
56 ResidualBuffer* top = top_;
57 top_ = top_->next_;
58 delete top;
59 }
60 }
61
Push(std::unique_ptr<ResidualBuffer> buffer)62 void ResidualBufferStack::Push(std::unique_ptr<ResidualBuffer> buffer) {
63 buffer->next_ = top_;
64 top_ = buffer.release();
65 ++num_buffers_;
66 }
67
Pop()68 std::unique_ptr<ResidualBuffer> ResidualBufferStack::Pop() {
69 std::unique_ptr<ResidualBuffer> top;
70 if (top_ != nullptr) {
71 top.reset(top_);
72 top_ = top_->next_;
73 top->next_ = nullptr;
74 --num_buffers_;
75 }
76 return top;
77 }
78
Swap(ResidualBufferStack * other)79 void ResidualBufferStack::Swap(ResidualBufferStack* other) {
80 std::swap(top_, other->top_);
81 std::swap(num_buffers_, other->num_buffers_);
82 }
83
ResidualBufferPool(bool use_128x128_superblock,int subsampling_x,int subsampling_y,size_t residual_size)84 ResidualBufferPool::ResidualBufferPool(bool use_128x128_superblock,
85 int subsampling_x, int subsampling_y,
86 size_t residual_size)
87 : buffer_size_(GetResidualBufferSize(
88 use_128x128_superblock ? 128 : 64, use_128x128_superblock ? 128 : 64,
89 subsampling_x, subsampling_y, residual_size)),
90 queue_size_(kMaxQueueSize[static_cast<int>(use_128x128_superblock)]
91 [subsampling_x][subsampling_y]) {}
92
Reset(bool use_128x128_superblock,int subsampling_x,int subsampling_y,size_t residual_size)93 void ResidualBufferPool::Reset(bool use_128x128_superblock, int subsampling_x,
94 int subsampling_y, size_t residual_size) {
95 const size_t buffer_size = GetResidualBufferSize(
96 use_128x128_superblock ? 128 : 64, use_128x128_superblock ? 128 : 64,
97 subsampling_x, subsampling_y, residual_size);
98 const int queue_size = kMaxQueueSize[static_cast<int>(use_128x128_superblock)]
99 [subsampling_x][subsampling_y];
100 if (buffer_size == buffer_size_ && queue_size == queue_size_) {
101 // The existing buffers (if any) are still valid, so don't do anything.
102 return;
103 }
104 buffer_size_ = buffer_size;
105 queue_size_ = queue_size;
106 // The existing buffers (if any) are no longer valid since the buffer size or
107 // the queue size has changed. Clear the stack.
108 ResidualBufferStack buffers;
109 {
110 std::lock_guard<std::mutex> lock(mutex_);
111 // Move the buffers in the stack to the local variable |buffers| and clear
112 // the stack.
113 buffers.Swap(&buffers_);
114 // Release mutex_ before freeing the buffers.
115 }
116 // As the local variable |buffers| goes out of scope, its destructor frees
117 // the buffers that were in the stack.
118 }
119
Get()120 std::unique_ptr<ResidualBuffer> ResidualBufferPool::Get() {
121 std::unique_ptr<ResidualBuffer> buffer = nullptr;
122 {
123 std::lock_guard<std::mutex> lock(mutex_);
124 buffer = buffers_.Pop();
125 }
126 if (buffer == nullptr) {
127 buffer = ResidualBuffer::Create(buffer_size_, queue_size_);
128 }
129 return buffer;
130 }
131
Release(std::unique_ptr<ResidualBuffer> buffer)132 void ResidualBufferPool::Release(std::unique_ptr<ResidualBuffer> buffer) {
133 buffer->transform_parameters()->Clear();
134 buffer->partition_tree_order()->Clear();
135 std::lock_guard<std::mutex> lock(mutex_);
136 buffers_.Push(std::move(buffer));
137 }
138
Size() const139 size_t ResidualBufferPool::Size() const {
140 std::lock_guard<std::mutex> lock(mutex_);
141 return buffers_.Size();
142 }
143
144 } // namespace libgav1
145