• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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