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