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 <mutex> // NOLINT (unapproved c++11 header)
18 #include <utility>
19
20 namespace libgav1 {
21 namespace {
22
23 // The maximum queue size is derived using the following formula:
24 // ((sb_size * sb_size) / 16) + (2 * (((sb_size / x) * (sb_size / y)) / 16)).
25 // Where:
26 // sb_size is the superblock size (64 or 128).
27 // 16 is 4*4 which is kMinTransformWidth * kMinTransformHeight.
28 // x is subsampling_x + 1.
29 // y is subsampling_y + 1.
30 // The first component is for the Y plane and the second component is for the U
31 // and V planes.
32 // For example, for 128x128 superblocks with 422 subsampling the size is:
33 // ((128 * 128) / 16) + (2 * (((128 / 2) * (128 / 1)) / 16)) = 2048.
34 //
35 // First dimension: use_128x128_superblock.
36 // Second dimension: subsampling_x.
37 // Third dimension: subsampling_y.
38 constexpr int kMaxQueueSize[2][2][2] = {
39 // 64x64 superblocks.
40 {
41 {768, 512},
42 {512, 384},
43 },
44 // 128x128 superblocks.
45 {
46 {3072, 2048},
47 {2048, 1536},
48 },
49 };
50
51 } // namespace
52
~ResidualBufferStack()53 ResidualBufferStack::~ResidualBufferStack() {
54 while (top_ != nullptr) {
55 ResidualBuffer* top = top_;
56 top_ = top_->next_;
57 delete top;
58 }
59 }
60
Push(std::unique_ptr<ResidualBuffer> buffer)61 void ResidualBufferStack::Push(std::unique_ptr<ResidualBuffer> buffer) {
62 buffer->next_ = top_;
63 top_ = buffer.release();
64 ++num_buffers_;
65 }
66
Pop()67 std::unique_ptr<ResidualBuffer> ResidualBufferStack::Pop() {
68 std::unique_ptr<ResidualBuffer> top;
69 if (top_ != nullptr) {
70 top.reset(top_);
71 top_ = top_->next_;
72 top->next_ = nullptr;
73 --num_buffers_;
74 }
75 return top;
76 }
77
Swap(ResidualBufferStack * other)78 void ResidualBufferStack::Swap(ResidualBufferStack* other) {
79 std::swap(top_, other->top_);
80 std::swap(num_buffers_, other->num_buffers_);
81 }
82
ResidualBufferPool(bool use_128x128_superblock,int subsampling_x,int subsampling_y,size_t residual_size)83 ResidualBufferPool::ResidualBufferPool(bool use_128x128_superblock,
84 int subsampling_x, int subsampling_y,
85 size_t residual_size)
86 : buffer_size_(GetResidualBufferSize(
87 use_128x128_superblock ? 128 : 64, use_128x128_superblock ? 128 : 64,
88 subsampling_x, subsampling_y, residual_size)),
89 queue_size_(kMaxQueueSize[static_cast<int>(use_128x128_superblock)]
90 [subsampling_x][subsampling_y]) {}
91
Reset(bool use_128x128_superblock,int subsampling_x,int subsampling_y,size_t residual_size)92 void ResidualBufferPool::Reset(bool use_128x128_superblock, int subsampling_x,
93 int subsampling_y, size_t residual_size) {
94 const size_t buffer_size = GetResidualBufferSize(
95 use_128x128_superblock ? 128 : 64, use_128x128_superblock ? 128 : 64,
96 subsampling_x, subsampling_y, residual_size);
97 const int queue_size = kMaxQueueSize[static_cast<int>(use_128x128_superblock)]
98 [subsampling_x][subsampling_y];
99 if (buffer_size == buffer_size_ && queue_size == queue_size_) {
100 // The existing buffers (if any) are still valid, so don't do anything.
101 return;
102 }
103 buffer_size_ = buffer_size;
104 queue_size_ = queue_size;
105 // The existing buffers (if any) are no longer valid since the buffer size or
106 // the queue size has changed. Clear the stack.
107 ResidualBufferStack buffers;
108 {
109 std::lock_guard<std::mutex> lock(mutex_);
110 // Move the buffers in the stack to the local variable |buffers| and clear
111 // the stack.
112 buffers.Swap(&buffers_);
113 // Release mutex_ before freeing the buffers.
114 }
115 // As the local variable |buffers| goes out of scope, its destructor frees
116 // the buffers that were in the stack.
117 }
118
Get()119 std::unique_ptr<ResidualBuffer> ResidualBufferPool::Get() {
120 std::unique_ptr<ResidualBuffer> buffer = nullptr;
121 {
122 std::lock_guard<std::mutex> lock(mutex_);
123 buffer = buffers_.Pop();
124 }
125 if (buffer == nullptr) {
126 buffer = ResidualBuffer::Create(buffer_size_, queue_size_);
127 }
128 return buffer;
129 }
130
Release(std::unique_ptr<ResidualBuffer> buffer)131 void ResidualBufferPool::Release(std::unique_ptr<ResidualBuffer> buffer) {
132 buffer->transform_parameters()->Reset();
133 std::lock_guard<std::mutex> lock(mutex_);
134 buffers_.Push(std::move(buffer));
135 }
136
Size() const137 size_t ResidualBufferPool::Size() const {
138 std::lock_guard<std::mutex> lock(mutex_);
139 return buffers_.Size();
140 }
141
142 } // namespace libgav1
143