blob: e1663920a3e0fc0478e2df27eaf1150a757855e1 [file] [log] [blame]
// Copyright 2019 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/residual_buffer_pool.h"
#include <mutex> // NOLINT (unapproved c++11 header)
#include <utility>
namespace libgav1 {
namespace {
// The maximum queue size is derived using the following formula:
// ((sb_size * sb_size) / 16) + (2 * (((sb_size / x) * (sb_size / y)) / 16)).
// Where:
// sb_size is the superblock size (64 or 128).
// 16 is 4*4 which is kMinTransformWidth * kMinTransformHeight.
// x is subsampling_x + 1.
// y is subsampling_y + 1.
// The first component is for the Y plane and the second component is for the U
// and V planes.
// For example, for 128x128 superblocks with 422 subsampling the size is:
// ((128 * 128) / 16) + (2 * (((128 / 2) * (128 / 1)) / 16)) = 2048.
//
// First dimension: use_128x128_superblock.
// Second dimension: subsampling_x.
// Third dimension: subsampling_y.
constexpr int kMaxQueueSize[2][2][2] = {
// 64x64 superblocks.
{
{768, 512},
{512, 384},
},
// 128x128 superblocks.
{
{3072, 2048},
{2048, 1536},
},
};
} // namespace
ResidualBufferStack::~ResidualBufferStack() {
while (top_ != nullptr) {
ResidualBuffer* top = top_;
top_ = top_->next_;
delete top;
}
}
void ResidualBufferStack::Push(std::unique_ptr<ResidualBuffer> buffer) {
buffer->next_ = top_;
top_ = buffer.release();
++num_buffers_;
}
std::unique_ptr<ResidualBuffer> ResidualBufferStack::Pop() {
std::unique_ptr<ResidualBuffer> top;
if (top_ != nullptr) {
top.reset(top_);
top_ = top_->next_;
top->next_ = nullptr;
--num_buffers_;
}
return top;
}
void ResidualBufferStack::Swap(ResidualBufferStack* other) {
std::swap(top_, other->top_);
std::swap(num_buffers_, other->num_buffers_);
}
ResidualBufferPool::ResidualBufferPool(bool use_128x128_superblock,
int subsampling_x, int subsampling_y,
size_t residual_size)
: buffer_size_(GetResidualBufferSize(
use_128x128_superblock ? 128 : 64, use_128x128_superblock ? 128 : 64,
subsampling_x, subsampling_y, residual_size)),
queue_size_(kMaxQueueSize[static_cast<int>(use_128x128_superblock)]
[subsampling_x][subsampling_y]) {}
void ResidualBufferPool::Reset(bool use_128x128_superblock, int subsampling_x,
int subsampling_y, size_t residual_size) {
const size_t buffer_size = GetResidualBufferSize(
use_128x128_superblock ? 128 : 64, use_128x128_superblock ? 128 : 64,
subsampling_x, subsampling_y, residual_size);
const int queue_size = kMaxQueueSize[static_cast<int>(use_128x128_superblock)]
[subsampling_x][subsampling_y];
if (buffer_size == buffer_size_ && queue_size == queue_size_) {
// The existing buffers (if any) are still valid, so don't do anything.
return;
}
buffer_size_ = buffer_size;
queue_size_ = queue_size;
// The existing buffers (if any) are no longer valid since the buffer size or
// the queue size has changed. Clear the stack.
ResidualBufferStack buffers;
{
std::lock_guard<std::mutex> lock(mutex_);
// Move the buffers in the stack to the local variable |buffers| and clear
// the stack.
buffers.Swap(&buffers_);
// Release mutex_ before freeing the buffers.
}
// As the local variable |buffers| goes out of scope, its destructor frees
// the buffers that were in the stack.
}
std::unique_ptr<ResidualBuffer> ResidualBufferPool::Get() {
std::unique_ptr<ResidualBuffer> buffer = nullptr;
{
std::lock_guard<std::mutex> lock(mutex_);
buffer = buffers_.Pop();
}
if (buffer == nullptr) {
buffer = ResidualBuffer::Create(buffer_size_, queue_size_);
}
return buffer;
}
void ResidualBufferPool::Release(std::unique_ptr<ResidualBuffer> buffer) {
buffer->transform_parameters()->Reset();
std::lock_guard<std::mutex> lock(mutex_);
buffers_.Push(std::move(buffer));
}
size_t ResidualBufferPool::Size() const {
std::lock_guard<std::mutex> lock(mutex_);
return buffers_.Size();
}
} // namespace libgav1