diff options
Diffstat (limited to 'src/buffer_pool.h')
-rw-r--r-- | src/buffer_pool.h | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/src/buffer_pool.h b/src/buffer_pool.h new file mode 100644 index 0000000..f35a633 --- /dev/null +++ b/src/buffer_pool.h @@ -0,0 +1,399 @@ +/* + * 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. + */ + +#ifndef LIBGAV1_SRC_BUFFER_POOL_H_ +#define LIBGAV1_SRC_BUFFER_POOL_H_ + +#include <array> +#include <cassert> +#include <climits> +#include <condition_variable> // NOLINT (unapproved c++11 header) +#include <cstdint> +#include <cstring> +#include <mutex> // NOLINT (unapproved c++11 header) + +#include "src/dsp/common.h" +#include "src/gav1/decoder_buffer.h" +#include "src/gav1/frame_buffer.h" +#include "src/internal_frame_buffer_list.h" +#include "src/symbol_decoder_context.h" +#include "src/utils/compiler_attributes.h" +#include "src/utils/constants.h" +#include "src/utils/reference_info.h" +#include "src/utils/segmentation.h" +#include "src/utils/segmentation_map.h" +#include "src/utils/types.h" +#include "src/utils/vector.h" +#include "src/yuv_buffer.h" + +namespace libgav1 { + +class BufferPool; + +enum FrameState : uint8_t { + kFrameStateUnknown, + kFrameStateStarted, + kFrameStateParsed, + kFrameStateDecoded +}; + +// A reference-counted frame buffer. Clients should access it via +// RefCountedBufferPtr, which manages reference counting transparently. +class RefCountedBuffer { + public: + // Not copyable or movable. + RefCountedBuffer(const RefCountedBuffer&) = delete; + RefCountedBuffer& operator=(const RefCountedBuffer&) = delete; + + // Allocates the YUV buffer. Returns true on success. Returns false on + // failure. This function ensures the thread safety of the |get_frame_buffer_| + // call (i.e.) only one |get_frame_buffer_| call will happen at a given time. + // TODO(b/142583029): In frame parallel mode, we can require the callbacks to + // be thread safe so that we can remove the thread safety of this function and + // applications can have fine grained locks. + // + // * |width| and |height| are the image dimensions in pixels. + // * |subsampling_x| and |subsampling_y| (either 0 or 1) specify the + // subsampling of the width and height of the chroma planes, respectively. + // * |left_border|, |right_border|, |top_border|, and |bottom_border| are + // the sizes (in pixels) of the borders on the left, right, top, and + // bottom sides, respectively. + // + // NOTE: The strides are a multiple of 16. Since the first row in each plane + // is 16-byte aligned, subsequent rows are also 16-byte aligned. + bool 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); + + YuvBuffer* buffer() { return &yuv_buffer_; } + + // Returns the buffer private data set by the get frame buffer callback when + // it allocated the YUV buffer. + void* buffer_private_data() const { + assert(buffer_private_data_valid_); + return buffer_private_data_; + } + + // NOTE: In the current frame, this is the frame_type syntax element in the + // frame header. In a reference frame, this implements the RefFrameType array + // in the spec. + FrameType frame_type() const { return frame_type_; } + void set_frame_type(FrameType frame_type) { frame_type_ = frame_type; } + + // The sample position for subsampled streams. This is the + // chroma_sample_position syntax element in the sequence header. + // + // NOTE: The decoder does not use chroma_sample_position, but it needs to be + // passed on to the client in DecoderBuffer. + ChromaSamplePosition chroma_sample_position() const { + return chroma_sample_position_; + } + void set_chroma_sample_position(ChromaSamplePosition chroma_sample_position) { + chroma_sample_position_ = chroma_sample_position; + } + + // Whether the frame can be used as show existing frame in the future. + bool showable_frame() const { return showable_frame_; } + void set_showable_frame(bool value) { showable_frame_ = value; } + + // Sets upscaled_width_, frame_width_, frame_height_, render_width_, + // render_height_, rows4x4_ and columns4x4_ from the corresponding fields + // in frame_header. Allocates reference_info_.motion_field_reference_frame, + // reference_info_.motion_field_mv_, and segmentation_map_. Returns true on + // success, false on failure. + bool SetFrameDimensions(const ObuFrameHeader& frame_header); + + int32_t upscaled_width() const { return upscaled_width_; } + int32_t frame_width() const { return frame_width_; } + int32_t frame_height() const { return frame_height_; } + // RenderWidth() and RenderHeight() return the render size, which is a hint + // to the application about the desired display size. + int32_t render_width() const { return render_width_; } + int32_t render_height() const { return render_height_; } + int32_t rows4x4() const { return rows4x4_; } + int32_t columns4x4() const { return columns4x4_; } + + int spatial_id() const { return spatial_id_; } + void set_spatial_id(int value) { spatial_id_ = value; } + int temporal_id() const { return temporal_id_; } + void set_temporal_id(int value) { temporal_id_ = value; } + + SegmentationMap* segmentation_map() { return &segmentation_map_; } + const SegmentationMap* segmentation_map() const { return &segmentation_map_; } + + // Only the |params| field of each GlobalMotion struct should be used. + const std::array<GlobalMotion, kNumReferenceFrameTypes>& GlobalMotions() + const { + return global_motion_; + } + // Saves the GlobalMotion array. Only the |params| field of each GlobalMotion + // struct is saved. + void SetGlobalMotions( + const std::array<GlobalMotion, kNumReferenceFrameTypes>& global_motions); + + // Returns the saved CDF tables. + const SymbolDecoderContext& FrameContext() const { return frame_context_; } + // Saves the CDF tables. The intra_frame_y_mode_cdf table is reset to the + // default. The last entry in each table, representing the symbol count for + // that context, is set to 0. + void SetFrameContext(const SymbolDecoderContext& context); + + const std::array<int8_t, kNumReferenceFrameTypes>& loop_filter_ref_deltas() + const { + return loop_filter_ref_deltas_; + } + const std::array<int8_t, kLoopFilterMaxModeDeltas>& loop_filter_mode_deltas() + const { + return loop_filter_mode_deltas_; + } + // Saves the ref_deltas and mode_deltas arrays in loop_filter. + void SetLoopFilterDeltas(const LoopFilter& loop_filter) { + loop_filter_ref_deltas_ = loop_filter.ref_deltas; + loop_filter_mode_deltas_ = loop_filter.mode_deltas; + } + + // Copies the saved values of the following fields to the Segmentation + // struct: feature_enabled, feature_data, segment_id_pre_skip, and + // last_active_segment_id. The other fields are left unchanged. + void GetSegmentationParameters(Segmentation* segmentation) const; + // Saves the feature_enabled, feature_data, segment_id_pre_skip, and + // last_active_segment_id fields of the Segmentation struct. + void SetSegmentationParameters(const Segmentation& segmentation); + + const FilmGrainParams& film_grain_params() const { + return film_grain_params_; + } + void set_film_grain_params(const FilmGrainParams& params) { + film_grain_params_ = params; + } + + const ReferenceInfo* reference_info() const { return &reference_info_; } + ReferenceInfo* reference_info() { return &reference_info_; } + + // This will wake up the WaitUntil*() functions and make them return false. + void Abort() { + { + std::lock_guard<std::mutex> lock(mutex_); + abort_ = true; + } + parsed_condvar_.notify_all(); + decoded_condvar_.notify_all(); + progress_row_condvar_.notify_all(); + } + + void SetFrameState(FrameState frame_state) { + { + std::lock_guard<std::mutex> lock(mutex_); + frame_state_ = frame_state; + } + if (frame_state == kFrameStateParsed) { + parsed_condvar_.notify_all(); + } else if (frame_state == kFrameStateDecoded) { + decoded_condvar_.notify_all(); + progress_row_condvar_.notify_all(); + } + } + + // Sets the progress of this frame to |progress_row| and notifies any threads + // that may be waiting on rows <= |progress_row|. + void SetProgress(int progress_row) { + { + std::lock_guard<std::mutex> lock(mutex_); + if (progress_row_ >= progress_row) return; + progress_row_ = progress_row; + } + progress_row_condvar_.notify_all(); + } + + void MarkFrameAsStarted() { + std::lock_guard<std::mutex> lock(mutex_); + if (frame_state_ != kFrameStateUnknown) return; + frame_state_ = kFrameStateStarted; + } + + // All the WaitUntil* functions will return true if the desired wait state was + // reached successfully. If the return value is false, then the caller must + // assume that the wait was not successful and try to stop whatever they are + // doing as early as possible. + + // Waits until the frame has been parsed. + bool WaitUntilParsed() { + std::unique_lock<std::mutex> lock(mutex_); + while (frame_state_ < kFrameStateParsed && !abort_) { + parsed_condvar_.wait(lock); + } + return !abort_; + } + + // Waits until the |progress_row| has been decoded (as indicated either by + // |progress_row_| or |frame_state_|). |progress_row_cache| must not be + // nullptr and will be populated with the value of |progress_row_| after the + // wait. + // + // Typical usage of |progress_row_cache| is as follows: + // * Initialize |*progress_row_cache| to INT_MIN. + // * Call WaitUntil only if |*progress_row_cache| < |progress_row|. + bool WaitUntil(int progress_row, int* progress_row_cache) { + // If |progress_row| is negative, it means that the wait is on the top + // border to be available. The top border will be available when row 0 has + // been decoded. So we can simply wait on row 0 instead. + progress_row = std::max(progress_row, 0); + std::unique_lock<std::mutex> lock(mutex_); + while (progress_row_ < progress_row && frame_state_ != kFrameStateDecoded && + !abort_) { + progress_row_condvar_.wait(lock); + } + // Once |frame_state_| reaches kFrameStateDecoded, |progress_row_| may no + // longer be updated. So we set |*progress_row_cache| to INT_MAX in that + // case. + *progress_row_cache = + (frame_state_ != kFrameStateDecoded) ? progress_row_ : INT_MAX; + return !abort_; + } + + // Waits until the entire frame has been decoded. + bool WaitUntilDecoded() { + std::unique_lock<std::mutex> lock(mutex_); + while (frame_state_ != kFrameStateDecoded && !abort_) { + decoded_condvar_.wait(lock); + } + return !abort_; + } + + private: + friend class BufferPool; + + // Methods for BufferPool: + RefCountedBuffer(); + ~RefCountedBuffer(); + void SetBufferPool(BufferPool* pool); + static void ReturnToBufferPool(RefCountedBuffer* ptr); + + BufferPool* pool_ = nullptr; + bool buffer_private_data_valid_ = false; + void* buffer_private_data_ = nullptr; + YuvBuffer yuv_buffer_; + bool in_use_ = false; // Only used by BufferPool. + + std::mutex mutex_; + FrameState frame_state_ = kFrameStateUnknown LIBGAV1_GUARDED_BY(mutex_); + int progress_row_ = -1 LIBGAV1_GUARDED_BY(mutex_); + // Signaled when progress_row_ is updated or when frame_state_ is set to + // kFrameStateDecoded. + std::condition_variable progress_row_condvar_; + // Signaled when the frame state is set to kFrameStateParsed. + std::condition_variable parsed_condvar_; + // Signaled when the frame state is set to kFrameStateDecoded. + std::condition_variable decoded_condvar_; + bool abort_ = false LIBGAV1_GUARDED_BY(mutex_); + + FrameType frame_type_ = kFrameKey; + ChromaSamplePosition chroma_sample_position_ = kChromaSamplePositionUnknown; + bool showable_frame_ = false; + + int32_t upscaled_width_ = 0; + int32_t frame_width_ = 0; + int32_t frame_height_ = 0; + int32_t render_width_ = 0; + int32_t render_height_ = 0; + int32_t columns4x4_ = 0; + int32_t rows4x4_ = 0; + int spatial_id_ = 0; + int temporal_id_ = 0; + + // segmentation_map_ contains a rows4x4_ by columns4x4_ 2D array. + SegmentationMap segmentation_map_; + + // Only the |params| field of each GlobalMotion struct is used. + // global_motion_[0] (for kReferenceFrameIntra) is not used. + std::array<GlobalMotion, kNumReferenceFrameTypes> global_motion_ = {}; + SymbolDecoderContext frame_context_; + std::array<int8_t, kNumReferenceFrameTypes> loop_filter_ref_deltas_; + std::array<int8_t, kLoopFilterMaxModeDeltas> loop_filter_mode_deltas_; + // Only the feature_enabled, feature_data, segment_id_pre_skip, and + // last_active_segment_id fields of the Segmentation struct are used. + // + // Note: The spec only requires that we save feature_enabled and + // feature_data. Since segment_id_pre_skip and last_active_segment_id depend + // on feature_enabled only, we also save their values as an optimization. + Segmentation segmentation_ = {}; + FilmGrainParams film_grain_params_ = {}; + ReferenceInfo reference_info_; +}; + +// RefCountedBufferPtr contains a reference to a RefCountedBuffer. +// +// Note: For simplicity, RefCountedBufferPtr is implemented as a +// std::shared_ptr<RefCountedBuffer>. This requires a heap allocation of the +// control block for std::shared_ptr. To avoid that heap allocation, we can +// add a |ref_count_| field to RefCountedBuffer and implement a custom +// RefCountedBufferPtr class. +using RefCountedBufferPtr = std::shared_ptr<RefCountedBuffer>; + +// BufferPool maintains a pool of RefCountedBuffers. +class BufferPool { + public: + BufferPool(FrameBufferSizeChangedCallback on_frame_buffer_size_changed, + GetFrameBufferCallback get_frame_buffer, + ReleaseFrameBufferCallback release_frame_buffer, + void* callback_private_data); + + // Not copyable or movable. + BufferPool(const BufferPool&) = delete; + BufferPool& operator=(const BufferPool&) = delete; + + ~BufferPool(); + + LIBGAV1_MUST_USE_RESULT bool OnFrameBufferSizeChanged( + int bitdepth, Libgav1ImageFormat image_format, int width, int height, + int left_border, int right_border, int top_border, int bottom_border); + + // Finds a free buffer in the buffer pool and returns a reference to the free + // buffer. If there is no free buffer, returns a null pointer. This function + // is thread safe. + RefCountedBufferPtr GetFreeBuffer(); + + // Aborts all the buffers that are in use. + void Abort(); + + private: + friend class RefCountedBuffer; + + // Returns an unused buffer to the buffer pool. Called by RefCountedBuffer + // only. This function is thread safe. + void ReturnUnusedBuffer(RefCountedBuffer* buffer); + + // Used to make the following functions thread safe: GetFreeBuffer(), + // ReturnUnusedBuffer(), RefCountedBuffer::Realloc(). + std::mutex mutex_; + + // Storing a RefCountedBuffer object in a Vector is complicated because of the + // copy/move semantics. So the simplest way around that is to store a list of + // pointers in the vector. + Vector<RefCountedBuffer*> buffers_ LIBGAV1_GUARDED_BY(mutex_); + InternalFrameBufferList internal_frame_buffers_; + + // Frame buffer callbacks. + FrameBufferSizeChangedCallback on_frame_buffer_size_changed_; + GetFrameBufferCallback get_frame_buffer_; + ReleaseFrameBufferCallback release_frame_buffer_; + // Private data associated with the frame buffer callbacks. + void* callback_private_data_; +}; + +} // namespace libgav1 + +#endif // LIBGAV1_SRC_BUFFER_POOL_H_ |