// 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/buffer_pool.h" #include #include #include "src/utils/common.h" #include "src/utils/constants.h" #include "src/utils/logging.h" namespace libgav1 { namespace { // Copies the feature_enabled, feature_data, segment_id_pre_skip, and // last_active_segment_id fields of Segmentation. void CopySegmentationParameters(const Segmentation& from, Segmentation* to) { memcpy(to->feature_enabled, from.feature_enabled, sizeof(to->feature_enabled)); memcpy(to->feature_data, from.feature_data, sizeof(to->feature_data)); to->segment_id_pre_skip = from.segment_id_pre_skip; to->last_active_segment_id = from.last_active_segment_id; } } // namespace RefCountedBuffer::RefCountedBuffer() = default; RefCountedBuffer::~RefCountedBuffer() = default; bool RefCountedBuffer::Realloc(int bitdepth, bool is_monochrome, int width, int height, int subsampling_x, int subsampling_y, int left_border, int right_border, int top_border, int bottom_border) { // The YuvBuffer::Realloc() could call the get frame buffer callback which // will need to be thread safe. So we ensure that we only call Realloc() once // at any given time. std::lock_guard lock(pool_->mutex_); assert(!buffer_private_data_valid_); if (!yuv_buffer_.Realloc( bitdepth, is_monochrome, width, height, subsampling_x, subsampling_y, left_border, right_border, top_border, bottom_border, pool_->get_frame_buffer_, pool_->callback_private_data_, &buffer_private_data_)) { return false; } buffer_private_data_valid_ = true; return true; } bool RefCountedBuffer::SetFrameDimensions(const ObuFrameHeader& frame_header) { upscaled_width_ = frame_header.upscaled_width; frame_width_ = frame_header.width; frame_height_ = frame_header.height; render_width_ = frame_header.render_width; render_height_ = frame_header.render_height; rows4x4_ = frame_header.rows4x4; columns4x4_ = frame_header.columns4x4; if (frame_header.refresh_frame_flags != 0 && !IsIntraFrame(frame_header.frame_type)) { const int rows4x4_half = DivideBy2(rows4x4_); const int columns4x4_half = DivideBy2(columns4x4_); if (!reference_info_.Reset(rows4x4_half, columns4x4_half)) { return false; } } return segmentation_map_.Allocate(rows4x4_, columns4x4_); } void RefCountedBuffer::SetGlobalMotions( const std::array& global_motions) { for (int ref = kReferenceFrameLast; ref <= kReferenceFrameAlternate; ++ref) { static_assert(sizeof(global_motion_[ref].params) == sizeof(global_motions[ref].params), ""); memcpy(global_motion_[ref].params, global_motions[ref].params, sizeof(global_motion_[ref].params)); } } void RefCountedBuffer::SetFrameContext(const SymbolDecoderContext& context) { frame_context_ = context; frame_context_.ResetIntraFrameYModeCdf(); frame_context_.ResetCounters(); } void RefCountedBuffer::GetSegmentationParameters( Segmentation* segmentation) const { CopySegmentationParameters(/*from=*/segmentation_, /*to=*/segmentation); } void RefCountedBuffer::SetSegmentationParameters( const Segmentation& segmentation) { CopySegmentationParameters(/*from=*/segmentation, /*to=*/&segmentation_); } void RefCountedBuffer::SetBufferPool(BufferPool* pool) { pool_ = pool; } void RefCountedBuffer::ReturnToBufferPool(RefCountedBuffer* ptr) { ptr->pool_->ReturnUnusedBuffer(ptr); } BufferPool::BufferPool( FrameBufferSizeChangedCallback on_frame_buffer_size_changed, GetFrameBufferCallback get_frame_buffer, ReleaseFrameBufferCallback release_frame_buffer, void* callback_private_data) { if (get_frame_buffer != nullptr) { // on_frame_buffer_size_changed may be null. assert(release_frame_buffer != nullptr); on_frame_buffer_size_changed_ = on_frame_buffer_size_changed; get_frame_buffer_ = get_frame_buffer; release_frame_buffer_ = release_frame_buffer; callback_private_data_ = callback_private_data; } else { on_frame_buffer_size_changed_ = OnInternalFrameBufferSizeChanged; get_frame_buffer_ = GetInternalFrameBuffer; release_frame_buffer_ = ReleaseInternalFrameBuffer; callback_private_data_ = &internal_frame_buffers_; } } BufferPool::~BufferPool() { for (const auto* buffer : buffers_) { if (buffer->in_use_) { assert(false && "RefCountedBuffer still in use at destruction time."); LIBGAV1_DLOG(ERROR, "RefCountedBuffer still in use at destruction time."); } delete buffer; } } bool BufferPool::OnFrameBufferSizeChanged(int bitdepth, Libgav1ImageFormat image_format, int width, int height, int left_border, int right_border, int top_border, int bottom_border) { if (on_frame_buffer_size_changed_ == nullptr) return true; return on_frame_buffer_size_changed_(callback_private_data_, bitdepth, image_format, width, height, left_border, right_border, top_border, bottom_border, /*stride_alignment=*/16) == kStatusOk; } RefCountedBufferPtr BufferPool::GetFreeBuffer() { // In frame parallel mode, the GetFreeBuffer() calls from ObuParser all happen // from the same thread serially, but the GetFreeBuffer() call in // DecoderImpl::ApplyFilmGrain can happen from multiple threads at the same // time. So this function has to be thread safe. // TODO(b/142583029): Investigate if the GetFreeBuffer() call in // DecoderImpl::ApplyFilmGrain() call can be serialized so that this function // need not be thread safe. std::unique_lock lock(mutex_); for (auto buffer : buffers_) { if (!buffer->in_use_) { buffer->in_use_ = true; buffer->progress_row_ = -1; buffer->frame_state_ = kFrameStateUnknown; lock.unlock(); return RefCountedBufferPtr(buffer, RefCountedBuffer::ReturnToBufferPool); } } lock.unlock(); auto* const buffer = new (std::nothrow) RefCountedBuffer(); if (buffer == nullptr) { LIBGAV1_DLOG(ERROR, "Failed to allocate a new reference counted buffer."); return RefCountedBufferPtr(); } buffer->SetBufferPool(this); buffer->in_use_ = true; buffer->progress_row_ = -1; buffer->frame_state_ = kFrameStateUnknown; lock.lock(); const bool ok = buffers_.push_back(buffer); lock.unlock(); if (!ok) { LIBGAV1_DLOG( ERROR, "Failed to push the new reference counted buffer into the vector."); delete buffer; return RefCountedBufferPtr(); } return RefCountedBufferPtr(buffer, RefCountedBuffer::ReturnToBufferPool); } void BufferPool::Abort() { std::unique_lock lock(mutex_); for (auto buffer : buffers_) { if (buffer->in_use_) { buffer->Abort(); } } } void BufferPool::ReturnUnusedBuffer(RefCountedBuffer* buffer) { std::lock_guard lock(mutex_); assert(buffer->in_use_); buffer->in_use_ = false; if (buffer->buffer_private_data_valid_) { release_frame_buffer_(callback_private_data_, buffer->buffer_private_data_); buffer->buffer_private_data_valid_ = false; } } } // namespace libgav1