| /* |
| * 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_TILE_H_ |
| #define LIBGAV1_SRC_TILE_H_ |
| |
| #include <algorithm> |
| #include <array> |
| #include <cassert> |
| #include <condition_variable> // NOLINT (unapproved c++11 header) |
| #include <cstddef> |
| #include <cstdint> |
| #include <memory> |
| #include <mutex> // NOLINT (unapproved c++11 header) |
| #include <vector> |
| |
| #include "src/buffer_pool.h" |
| #include "src/decoder_state.h" |
| #include "src/dsp/common.h" |
| #include "src/dsp/constants.h" |
| #include "src/dsp/dsp.h" |
| #include "src/frame_scratch_buffer.h" |
| #include "src/loop_restoration_info.h" |
| #include "src/obu_parser.h" |
| #include "src/post_filter.h" |
| #include "src/quantizer.h" |
| #include "src/residual_buffer_pool.h" |
| #include "src/symbol_decoder_context.h" |
| #include "src/tile_scratch_buffer.h" |
| #include "src/utils/array_2d.h" |
| #include "src/utils/block_parameters_holder.h" |
| #include "src/utils/blocking_counter.h" |
| #include "src/utils/common.h" |
| #include "src/utils/compiler_attributes.h" |
| #include "src/utils/constants.h" |
| #include "src/utils/entropy_decoder.h" |
| #include "src/utils/memory.h" |
| #include "src/utils/parameter_tree.h" |
| #include "src/utils/segmentation_map.h" |
| #include "src/utils/threadpool.h" |
| #include "src/utils/types.h" |
| #include "src/yuv_buffer.h" |
| |
| namespace libgav1 { |
| |
| // Indicates what the ProcessSuperBlock() and TransformBlock() functions should |
| // do. "Parse" refers to consuming the bitstream, reading the transform |
| // coefficients and performing the dequantization. "Decode" refers to computing |
| // the prediction, applying the inverse transforms and adding the residual. |
| enum ProcessingMode { |
| kProcessingModeParseOnly, |
| kProcessingModeDecodeOnly, |
| kProcessingModeParseAndDecode, |
| }; |
| |
| class Tile : public Allocable { |
| public: |
| static std::unique_ptr<Tile> Create( |
| int tile_number, const uint8_t* const data, size_t size, |
| const ObuSequenceHeader& sequence_header, |
| const ObuFrameHeader& frame_header, RefCountedBuffer* const current_frame, |
| const DecoderState& state, FrameScratchBuffer* const frame_scratch_buffer, |
| const WedgeMaskArray& wedge_masks, |
| SymbolDecoderContext* const saved_symbol_decoder_context, |
| const SegmentationMap* prev_segment_ids, PostFilter* const post_filter, |
| const dsp::Dsp* const dsp, ThreadPool* const thread_pool, |
| BlockingCounterWithStatus* const pending_tiles, bool frame_parallel, |
| bool use_intra_prediction_buffer) { |
| std::unique_ptr<Tile> tile(new (std::nothrow) Tile( |
| tile_number, data, size, sequence_header, frame_header, current_frame, |
| state, frame_scratch_buffer, wedge_masks, saved_symbol_decoder_context, |
| prev_segment_ids, post_filter, dsp, thread_pool, pending_tiles, |
| frame_parallel, use_intra_prediction_buffer)); |
| return (tile != nullptr && tile->Init()) ? std::move(tile) : nullptr; |
| } |
| |
| // Move only. |
| Tile(Tile&& tile) noexcept; |
| Tile& operator=(Tile&& tile) noexcept; |
| Tile(const Tile&) = delete; |
| Tile& operator=(const Tile&) = delete; |
| |
| struct Block; // Defined after this class. |
| |
| // Parses the entire tile. |
| bool Parse(); |
| // Decodes the entire tile. |superblock_row_progress| and |
| // |superblock_row_progress_condvar| are arrays of size equal to the number of |
| // superblock rows in the frame. Increments |superblock_row_progress[i]| after |
| // each superblock row at index |i| is decoded. If the count reaches the |
| // number of tile columns, then it notifies |
| // |superblock_row_progress_condvar[i]|. |
| bool Decode(std::mutex* mutex, int* superblock_row_progress, |
| std::condition_variable* superblock_row_progress_condvar); |
| // Parses and decodes the entire tile. Depending on the configuration of this |
| // Tile, this function may do multithreaded decoding. |
| bool ParseAndDecode(); // 5.11.2. |
| // Processes all the columns of the superblock row at |row4x4| that are within |
| // this Tile. If |save_symbol_decoder_context| is true, then |
| // SaveSymbolDecoderContext() is invoked for the last superblock row. |
| template <ProcessingMode processing_mode, bool save_symbol_decoder_context> |
| bool ProcessSuperBlockRow(int row4x4, TileScratchBuffer* scratch_buffer); |
| |
| const ObuSequenceHeader& sequence_header() const { return sequence_header_; } |
| const ObuFrameHeader& frame_header() const { return frame_header_; } |
| const RefCountedBuffer& current_frame() const { return current_frame_; } |
| const TemporalMotionField& motion_field() const { return motion_field_; } |
| const std::array<bool, kNumReferenceFrameTypes>& reference_frame_sign_bias() |
| const { |
| return reference_frame_sign_bias_; |
| } |
| |
| bool IsRow4x4Inside(int row4x4) const { |
| return row4x4 >= row4x4_start_ && row4x4 < row4x4_end_; |
| } |
| |
| // 5.11.51. |
| bool IsInside(int row4x4, int column4x4) const { |
| return IsRow4x4Inside(row4x4) && column4x4 >= column4x4_start_ && |
| column4x4 < column4x4_end_; |
| } |
| |
| bool IsLeftInside(int column4x4) const { |
| // We use "larger than" as the condition. Don't pass in the left column |
| // offset column4x4 - 1. |
| assert(column4x4 <= column4x4_end_); |
| return column4x4 > column4x4_start_; |
| } |
| |
| bool IsTopInside(int row4x4) const { |
| // We use "larger than" as the condition. Don't pass in the top row offset |
| // row4x4 - 1. |
| assert(row4x4 <= row4x4_end_); |
| return row4x4 > row4x4_start_; |
| } |
| |
| bool IsTopLeftInside(int row4x4, int column4x4) const { |
| // We use "larger than" as the condition. Don't pass in the top row offset |
| // row4x4 - 1 or the left column offset column4x4 - 1. |
| assert(row4x4 <= row4x4_end_); |
| assert(column4x4 <= column4x4_end_); |
| return row4x4 > row4x4_start_ && column4x4 > column4x4_start_; |
| } |
| |
| bool IsBottomRightInside(int row4x4, int column4x4) const { |
| assert(row4x4 >= row4x4_start_); |
| assert(column4x4 >= column4x4_start_); |
| return row4x4 < row4x4_end_ && column4x4 < column4x4_end_; |
| } |
| |
| BlockParameters** BlockParametersAddress(int row4x4, int column4x4) const { |
| return block_parameters_holder_.Address(row4x4, column4x4); |
| } |
| |
| int BlockParametersStride() const { |
| return block_parameters_holder_.columns4x4(); |
| } |
| |
| // Returns true if Parameters() can be called with |row| and |column| as |
| // inputs, false otherwise. |
| bool HasParameters(int row, int column) const { |
| return block_parameters_holder_.Find(row, column) != nullptr; |
| } |
| const BlockParameters& Parameters(int row, int column) const { |
| return *block_parameters_holder_.Find(row, column); |
| } |
| |
| int number() const { return number_; } |
| int superblock_rows() const { return superblock_rows_; } |
| int superblock_columns() const { return superblock_columns_; } |
| int row4x4_start() const { return row4x4_start_; } |
| int column4x4_start() const { return column4x4_start_; } |
| int column4x4_end() const { return column4x4_end_; } |
| |
| private: |
| Tile(int tile_number, const uint8_t* data, size_t size, |
| const ObuSequenceHeader& sequence_header, |
| const ObuFrameHeader& frame_header, RefCountedBuffer* current_frame, |
| const DecoderState& state, FrameScratchBuffer* frame_scratch_buffer, |
| const WedgeMaskArray& wedge_masks, |
| SymbolDecoderContext* saved_symbol_decoder_context, |
| const SegmentationMap* prev_segment_ids, PostFilter* post_filter, |
| const dsp::Dsp* dsp, ThreadPool* thread_pool, |
| BlockingCounterWithStatus* pending_tiles, bool frame_parallel, |
| bool use_intra_prediction_buffer); |
| |
| // Stores the transform tree state when reading variable size transform trees |
| // and when applying the transform tree. When applying the transform tree, |
| // |depth| is not used. |
| struct TransformTreeNode { |
| // The default constructor is invoked by the Stack<TransformTreeNode, n> |
| // constructor. Stack<> does not use the default-constructed elements, so it |
| // is safe for the default constructor to not initialize the members. |
| TransformTreeNode() = default; |
| TransformTreeNode(int x, int y, TransformSize tx_size, int depth = -1) |
| : x(x), y(y), tx_size(tx_size), depth(depth) {} |
| |
| int x; |
| int y; |
| TransformSize tx_size; |
| int depth; |
| }; |
| |
| // Enum to track the processing state of a superblock. |
| enum SuperBlockState : uint8_t { |
| kSuperBlockStateNone, // Not yet parsed or decoded. |
| kSuperBlockStateParsed, // Parsed but not yet decoded. |
| kSuperBlockStateScheduled, // Scheduled for decoding. |
| kSuperBlockStateDecoded // Parsed and decoded. |
| }; |
| |
| // Parameters used to facilitate multi-threading within the Tile. |
| struct ThreadingParameters { |
| std::mutex mutex; |
| // 2d array of size |superblock_rows_| by |superblock_columns_| containing |
| // the processing state of each superblock. |
| Array2D<SuperBlockState> sb_state LIBGAV1_GUARDED_BY(mutex); |
| // Variable used to indicate either parse or decode failure. |
| bool abort LIBGAV1_GUARDED_BY(mutex) = false; |
| int pending_jobs LIBGAV1_GUARDED_BY(mutex) = 0; |
| std::condition_variable pending_jobs_zero_condvar; |
| }; |
| |
| // The residual pointer is used to traverse the |residual_buffer_|. It is |
| // used in two different ways. |
| // If |split_parse_and_decode_| is true: |
| // The pointer points to the beginning of the |residual_buffer_| when the |
| // "parse" and "decode" steps begin. It is then moved forward tx_size in |
| // each iteration of the "parse" and the "decode" steps. In this case, the |
| // ResidualPtr variable passed into various functions starting from |
| // ProcessSuperBlock is used as an in/out parameter to keep track of the |
| // residual pointer. |
| // If |split_parse_and_decode_| is false: |
| // The pointer is reset to the beginning of the |residual_buffer_| for |
| // every transform block. |
| using ResidualPtr = uint8_t*; |
| |
| // Performs member initializations that may fail. Helper function used by |
| // Create(). |
| LIBGAV1_MUST_USE_RESULT bool Init(); |
| |
| // Saves the symbol decoder context of this tile into |
| // |saved_symbol_decoder_context_| if necessary. |
| void SaveSymbolDecoderContext(); |
| |
| // Entry point for multi-threaded decoding. This function performs the same |
| // functionality as ParseAndDecode(). The current thread does the "parse" step |
| // while the worker threads do the "decode" step. |
| bool ThreadedParseAndDecode(); |
| |
| // Returns whether or not the prerequisites for decoding the superblock at |
| // |row_index| and |column_index| are satisfied. |threading_.mutex| must be |
| // held when calling this function. |
| bool CanDecode(int row_index, int column_index) const; |
| |
| // This function is run by the worker threads when multi-threaded decoding is |
| // enabled. Once a superblock is decoded, this function will set the |
| // corresponding |threading_.sb_state| entry to kSuperBlockStateDecoded. On |
| // failure, |threading_.abort| will be set to true. If at any point |
| // |threading_.abort| becomes true, this function will return as early as it |
| // can. If the decoding succeeds, this function will also schedule the |
| // decoding jobs for the superblock to the bottom-left and the superblock to |
| // the right of this superblock (if it is allowed). |
| void DecodeSuperBlock(int row_index, int column_index, int block_width4x4); |
| |
| // If |use_intra_prediction_buffer_| is true, then this function copies the |
| // last row of the superblockrow starting at |row4x4| into the |
| // |intra_prediction_buffer_| (which may be used by the intra prediction |
| // process for the next superblock row). |
| void PopulateIntraPredictionBuffer(int row4x4); |
| |
| uint16_t* GetPartitionCdf(int row4x4, int column4x4, BlockSize block_size); |
| bool ReadPartition(int row4x4, int column4x4, BlockSize block_size, |
| bool has_rows, bool has_columns, Partition* partition); |
| // Processes the Partition starting at |row4x4_start|, |column4x4_start| |
| // iteratively. It performs a DFS traversal over the partition tree to process |
| // the blocks in the right order. |
| bool ProcessPartition( |
| int row4x4_start, int column4x4_start, ParameterTree* root, |
| TileScratchBuffer* scratch_buffer, |
| ResidualPtr* residual); // Iterative implementation of 5.11.4. |
| bool ProcessBlock(int row4x4, int column4x4, BlockSize block_size, |
| ParameterTree* tree, TileScratchBuffer* scratch_buffer, |
| ResidualPtr* residual); // 5.11.5. |
| void ResetCdef(int row4x4, int column4x4); // 5.11.55. |
| |
| // This function is used to decode a superblock when the parsing has already |
| // been done for that superblock. |
| bool DecodeSuperBlock(ParameterTree* tree, TileScratchBuffer* scratch_buffer, |
| ResidualPtr* residual); |
| // Helper function used by DecodeSuperBlock(). Note that the decode_block() |
| // function in the spec is equivalent to ProcessBlock() in the code. |
| bool DecodeBlock(ParameterTree* tree, TileScratchBuffer* scratch_buffer, |
| ResidualPtr* residual); |
| |
| void ClearBlockDecoded(TileScratchBuffer* scratch_buffer, int row4x4, |
| int column4x4); // 5.11.3. |
| bool ProcessSuperBlock(int row4x4, int column4x4, int block_width4x4, |
| TileScratchBuffer* scratch_buffer, |
| ProcessingMode mode); |
| void ResetLoopRestorationParams(); |
| void ReadLoopRestorationCoefficients(int row4x4, int column4x4, |
| BlockSize block_size); // 5.11.57. |
| |
| // Helper functions for DecodeBlock. |
| bool ReadSegmentId(const Block& block); // 5.11.9. |
| bool ReadIntraSegmentId(const Block& block); // 5.11.8. |
| void ReadSkip(const Block& block); // 5.11.11. |
| void ReadSkipMode(const Block& block); // 5.11.10. |
| void ReadCdef(const Block& block); // 5.11.56. |
| // Returns the new value. |cdf| is an array of size kDeltaSymbolCount + 1. |
| int ReadAndClipDelta(uint16_t* cdf, int delta_small, int scale, int min_value, |
| int max_value, int value); |
| void ReadQuantizerIndexDelta(const Block& block); // 5.11.12. |
| void ReadLoopFilterDelta(const Block& block); // 5.11.13. |
| // Populates |BlockParameters::deblock_filter_level| for the given |block| |
| // using |deblock_filter_levels_|. |
| void PopulateDeblockFilterLevel(const Block& block); |
| void ReadPredictionModeY(const Block& block, bool intra_y_mode); |
| void ReadIntraAngleInfo(const Block& block, |
| PlaneType plane_type); // 5.11.42 and 5.11.43. |
| void ReadPredictionModeUV(const Block& block); |
| void ReadCflAlpha(const Block& block); // 5.11.45. |
| int GetPaletteCache(const Block& block, PlaneType plane_type, |
| uint16_t* cache); |
| void ReadPaletteColors(const Block& block, Plane plane); |
| void ReadPaletteModeInfo(const Block& block); // 5.11.46. |
| void ReadFilterIntraModeInfo(const Block& block); // 5.11.24. |
| int ReadMotionVectorComponent(const Block& block, |
| int component); // 5.11.32. |
| void ReadMotionVector(const Block& block, int index); // 5.11.31. |
| bool DecodeIntraModeInfo(const Block& block); // 5.11.7. |
| int8_t ComputePredictedSegmentId(const Block& block) const; // 5.11.21. |
| bool ReadInterSegmentId(const Block& block, bool pre_skip); // 5.11.19. |
| void ReadIsInter(const Block& block); // 5.11.20. |
| bool ReadIntraBlockModeInfo(const Block& block, |
| bool intra_y_mode); // 5.11.22. |
| CompoundReferenceType ReadCompoundReferenceType(const Block& block); |
| template <bool is_single, bool is_backward, int index> |
| uint16_t* GetReferenceCdf(const Block& block, CompoundReferenceType type = |
| kNumCompoundReferenceTypes); |
| void ReadReferenceFrames(const Block& block); // 5.11.25. |
| void ReadInterPredictionModeY(const Block& block, |
| const MvContexts& mode_contexts); |
| void ReadRefMvIndex(const Block& block); |
| void ReadInterIntraMode(const Block& block, bool is_compound); // 5.11.28. |
| bool IsScaled(ReferenceFrameType type) const; // Part of 5.11.27. |
| void ReadMotionMode(const Block& block, bool is_compound); // 5.11.27. |
| uint16_t* GetIsExplicitCompoundTypeCdf(const Block& block); |
| uint16_t* GetIsCompoundTypeAverageCdf(const Block& block); |
| void ReadCompoundType(const Block& block, bool is_compound); // 5.11.29. |
| uint16_t* GetInterpolationFilterCdf(const Block& block, int direction); |
| void ReadInterpolationFilter(const Block& block); |
| bool ReadInterBlockModeInfo(const Block& block); // 5.11.23. |
| bool DecodeInterModeInfo(const Block& block); // 5.11.18. |
| bool DecodeModeInfo(const Block& block); // 5.11.6. |
| bool IsMvValid(const Block& block, bool is_compound) const; // 6.10.25. |
| bool AssignInterMv(const Block& block, bool is_compound); // 5.11.26. |
| bool AssignIntraMv(const Block& block); // 5.11.26. |
| int GetTopTransformWidth(const Block& block, int row4x4, int column4x4, |
| bool ignore_skip); |
| int GetLeftTransformHeight(const Block& block, int row4x4, int column4x4, |
| bool ignore_skip); |
| TransformSize ReadFixedTransformSize(const Block& block); // 5.11.15. |
| // Iterative implementation of 5.11.17. |
| void ReadVariableTransformTree(const Block& block, int row4x4, int column4x4, |
| TransformSize tx_size); |
| void DecodeTransformSize(const Block& block); // 5.11.16. |
| bool ComputePrediction(const Block& block); // 5.11.33. |
| // |x4| and |y4| are the column and row positions of the 4x4 block. |w4| and |
| // |h4| are the width and height in 4x4 units of |tx_size|. |
| int GetTransformAllZeroContext(const Block& block, Plane plane, |
| TransformSize tx_size, int x4, int y4, int w4, |
| int h4); |
| TransformSet GetTransformSet(TransformSize tx_size, |
| bool is_inter) const; // 5.11.48. |
| TransformType ComputeTransformType(const Block& block, Plane plane, |
| TransformSize tx_size, int block_x, |
| int block_y); // 5.11.40. |
| void ReadTransformType(const Block& block, int x4, int y4, |
| TransformSize tx_size); // 5.11.47. |
| template <typename ResidualType> |
| void ReadCoeffBase2D( |
| const uint16_t* scan, PlaneType plane_type, TransformSize tx_size, |
| int clamped_tx_size_context, int adjusted_tx_width_log2, int eob, |
| uint16_t coeff_base_cdf[kCoeffBaseContexts][kCoeffBaseSymbolCount + 1], |
| ResidualType* quantized_buffer); |
| template <typename ResidualType> |
| void ReadCoeffBaseHorizontal( |
| const uint16_t* scan, PlaneType plane_type, TransformSize tx_size, |
| int clamped_tx_size_context, int adjusted_tx_width_log2, int eob, |
| uint16_t coeff_base_cdf[kCoeffBaseContexts][kCoeffBaseSymbolCount + 1], |
| ResidualType* quantized_buffer); |
| template <typename ResidualType> |
| void ReadCoeffBaseVertical( |
| const uint16_t* scan, PlaneType plane_type, TransformSize tx_size, |
| int clamped_tx_size_context, int adjusted_tx_width_log2, int eob, |
| uint16_t coeff_base_cdf[kCoeffBaseContexts][kCoeffBaseSymbolCount + 1], |
| ResidualType* quantized_buffer); |
| int GetDcSignContext(int x4, int y4, int w4, int h4, Plane plane); |
| void SetEntropyContexts(int x4, int y4, int w4, int h4, Plane plane, |
| uint8_t coefficient_level, int8_t dc_category); |
| void InterIntraPrediction( |
| uint16_t* prediction_0, const uint8_t* prediction_mask, |
| ptrdiff_t prediction_mask_stride, |
| const PredictionParameters& prediction_parameters, int prediction_width, |
| int prediction_height, int subsampling_x, int subsampling_y, |
| uint8_t* dest, |
| ptrdiff_t dest_stride); // Part of section 7.11.3.1 in the spec. |
| void CompoundInterPrediction( |
| const Block& block, const uint8_t* prediction_mask, |
| ptrdiff_t prediction_mask_stride, int prediction_width, |
| int prediction_height, int subsampling_x, int subsampling_y, |
| int candidate_row, int candidate_column, uint8_t* dest, |
| ptrdiff_t dest_stride); // Part of section 7.11.3.1 in the spec. |
| GlobalMotion* GetWarpParams(const Block& block, Plane plane, |
| int prediction_width, int prediction_height, |
| const PredictionParameters& prediction_parameters, |
| ReferenceFrameType reference_type, |
| bool* is_local_valid, |
| GlobalMotion* global_motion_params, |
| GlobalMotion* local_warp_params) |
| const; // Part of section 7.11.3.1 in the spec. |
| bool InterPrediction(const Block& block, Plane plane, int x, int y, |
| int prediction_width, int prediction_height, |
| int candidate_row, int candidate_column, |
| bool* is_local_valid, |
| GlobalMotion* local_warp_params); // 7.11.3.1. |
| void ScaleMotionVector(const MotionVector& mv, Plane plane, |
| int reference_frame_index, int x, int y, int* start_x, |
| int* start_y, int* step_x, int* step_y); // 7.11.3.3. |
| // If the method returns false, the caller only uses the output parameters |
| // *ref_block_start_x and *ref_block_start_y. If the method returns true, the |
| // caller uses all three output parameters. |
| static bool GetReferenceBlockPosition( |
| int reference_frame_index, bool is_scaled, int width, int height, |
| int ref_start_x, int ref_last_x, int ref_start_y, int ref_last_y, |
| int start_x, int start_y, int step_x, int step_y, int left_border, |
| int right_border, int top_border, int bottom_border, |
| int* ref_block_start_x, int* ref_block_start_y, int* ref_block_end_x); |
| |
| template <typename Pixel> |
| void BuildConvolveBlock(Plane plane, int reference_frame_index, |
| bool is_scaled, int height, int ref_start_x, |
| int ref_last_x, int ref_start_y, int ref_last_y, |
| int step_y, int ref_block_start_x, |
| int ref_block_end_x, int ref_block_start_y, |
| uint8_t* block_buffer, |
| ptrdiff_t convolve_buffer_stride, |
| ptrdiff_t block_extended_width); |
| bool BlockInterPrediction(const Block& block, Plane plane, |
| int reference_frame_index, const MotionVector& mv, |
| int x, int y, int width, int height, |
| int candidate_row, int candidate_column, |
| uint16_t* prediction, bool is_compound, |
| bool is_inter_intra, uint8_t* dest, |
| ptrdiff_t dest_stride); // 7.11.3.4. |
| bool BlockWarpProcess(const Block& block, Plane plane, int index, |
| int block_start_x, int block_start_y, int width, |
| int height, GlobalMotion* warp_params, bool is_compound, |
| bool is_inter_intra, uint8_t* dest, |
| ptrdiff_t dest_stride); // 7.11.3.5. |
| bool ObmcBlockPrediction(const Block& block, const MotionVector& mv, |
| Plane plane, int reference_frame_index, int width, |
| int height, int x, int y, int candidate_row, |
| int candidate_column, |
| ObmcDirection blending_direction); |
| bool ObmcPrediction(const Block& block, Plane plane, int width, |
| int height); // 7.11.3.9. |
| void DistanceWeightedPrediction(void* prediction_0, void* prediction_1, |
| int width, int height, int candidate_row, |
| int candidate_column, uint8_t* dest, |
| ptrdiff_t dest_stride); // 7.11.3.15. |
| // This function specializes the parsing of DC coefficient by removing some of |
| // the branches when i == 0 (since scan[0] is always 0 and scan[i] is always |
| // non-zero for all other possible values of i). |dc_category| is an output |
| // parameter that is populated when |is_dc_coefficient| is true. |
| // |coefficient_level| is an output parameter which accumulates the |
| // coefficient level. |
| template <typename ResidualType, bool is_dc_coefficient> |
| LIBGAV1_ALWAYS_INLINE bool ReadSignAndApplyDequantization( |
| const uint16_t* scan, int i, int q_value, const uint8_t* quantizer_matrix, |
| int shift, int max_value, uint16_t* dc_sign_cdf, int8_t* dc_category, |
| int* coefficient_level, |
| ResidualType* residual_buffer); // Part of 5.11.39. |
| int ReadCoeffBaseRange(int clamped_tx_size_context, int cdf_context, |
| int plane_type); // Part of 5.11.39. |
| // Returns the number of non-zero coefficients that were read. |tx_type| is an |
| // output parameter that stores the computed transform type for the plane |
| // whose coefficients were read. Returns -1 on failure. |
| template <typename ResidualType> |
| int ReadTransformCoefficients(const Block& block, Plane plane, int start_x, |
| int start_y, TransformSize tx_size, |
| TransformType* tx_type); // 5.11.39. |
| bool TransformBlock(const Block& block, Plane plane, int base_x, int base_y, |
| TransformSize tx_size, int x, int y, |
| ProcessingMode mode); // 5.11.35. |
| // Iterative implementation of 5.11.36. |
| bool TransformTree(const Block& block, int start_x, int start_y, |
| BlockSize plane_size, ProcessingMode mode); |
| void ReconstructBlock(const Block& block, Plane plane, int start_x, |
| int start_y, TransformSize tx_size, |
| TransformType tx_type, |
| int non_zero_coeff_count); // Part of 7.12.3. |
| bool Residual(const Block& block, ProcessingMode mode); // 5.11.34. |
| // part of 5.11.5 (reset_block_context() in the spec). |
| void ResetEntropyContext(const Block& block); |
| // Populates the |color_context| and |color_order| for the |i|th iteration |
| // with entries counting down from |start| to |end| (|start| > |end|). |
| void PopulatePaletteColorContexts( |
| const Block& block, PlaneType plane_type, int i, int start, int end, |
| uint8_t color_order[kMaxPaletteSquare][kMaxPaletteSize], |
| uint8_t color_context[kMaxPaletteSquare]); // 5.11.50. |
| bool ReadPaletteTokens(const Block& block); // 5.11.49. |
| template <typename Pixel> |
| void IntraPrediction(const Block& block, Plane plane, int x, int y, |
| bool has_left, bool has_top, bool has_top_right, |
| bool has_bottom_left, PredictionMode mode, |
| TransformSize tx_size); |
| bool IsSmoothPrediction(int row, int column, Plane plane) const; |
| int GetIntraEdgeFilterType(const Block& block, |
| Plane plane) const; // 7.11.2.8. |
| template <typename Pixel> |
| void DirectionalPrediction(const Block& block, Plane plane, int x, int y, |
| bool has_left, bool has_top, bool needs_left, |
| bool needs_top, int prediction_angle, int width, |
| int height, int max_x, int max_y, |
| TransformSize tx_size, Pixel* top_row, |
| Pixel* left_column); // 7.11.2.4. |
| template <typename Pixel> |
| void PalettePrediction(const Block& block, Plane plane, int start_x, |
| int start_y, int x, int y, |
| TransformSize tx_size); // 7.11.4. |
| template <typename Pixel> |
| void ChromaFromLumaPrediction(const Block& block, Plane plane, int start_x, |
| int start_y, |
| TransformSize tx_size); // 7.11.5. |
| // Section 7.19. Applies some filtering and reordering to the motion vectors |
| // for the given |block| and stores them into |current_frame_|. |
| void StoreMotionFieldMvsIntoCurrentFrame(const Block& block); |
| |
| // Returns the zero-based index of the super block that contains |row4x4| |
| // relative to the start of this tile. |
| int SuperBlockRowIndex(int row4x4) const { |
| return (row4x4 - row4x4_start_) >> |
| (sequence_header_.use_128x128_superblock ? 5 : 4); |
| } |
| |
| // Returns the zero-based index of the super block that contains |column4x4| |
| // relative to the start of this tile. |
| int SuperBlockColumnIndex(int column4x4) const { |
| return (column4x4 - column4x4_start_) >> |
| (sequence_header_.use_128x128_superblock ? 5 : 4); |
| } |
| |
| BlockSize SuperBlockSize() const { |
| return sequence_header_.use_128x128_superblock ? kBlock128x128 |
| : kBlock64x64; |
| } |
| int PlaneCount() const { |
| return sequence_header_.color_config.is_monochrome ? kMaxPlanesMonochrome |
| : kMaxPlanes; |
| } |
| |
| const int number_; |
| const int row_; |
| const int column_; |
| const uint8_t* const data_; |
| size_t size_; |
| int row4x4_start_; |
| int row4x4_end_; |
| int column4x4_start_; |
| int column4x4_end_; |
| int superblock_rows_; |
| int superblock_columns_; |
| bool read_deltas_; |
| const int8_t subsampling_x_[kMaxPlanes]; |
| const int8_t subsampling_y_[kMaxPlanes]; |
| int deblock_row_limit_[kMaxPlanes]; |
| int deblock_column_limit_[kMaxPlanes]; |
| |
| // The dimensions (in order) are: segment_id, level_index (based on plane and |
| // direction), reference_frame and mode_id. |
| uint8_t deblock_filter_levels_[kMaxSegments][kFrameLfCount] |
| [kNumReferenceFrameTypes][2]; |
| |
| // current_quantizer_index_ is in the range [0, 255]. |
| uint8_t current_quantizer_index_; |
| // These two arrays (|coefficient_levels_| and |dc_categories_|) are used to |
| // store the entropy context. Their dimensions are as follows: First - |
| // left/top; Second - plane; Third - row4x4 (if first dimension is |
| // left)/column4x4 (if first dimension is top). |
| // |
| // This is equivalent to the LeftLevelContext and AboveLevelContext arrays in |
| // the spec. In the spec, it stores values from 0 through 63 (inclusive). The |
| // stored values are used to compute the left and top contexts in |
| // GetTransformAllZeroContext. In that function, we only care about the |
| // following values: 0, 1, 2, 3 and >= 4. So instead of clamping to 63, we |
| // clamp to 4 (i.e.) all the values greater than 4 are stored as 4. |
| std::array<Array2D<uint8_t>, 2> coefficient_levels_; |
| // This is equivalent to the LeftDcContext and AboveDcContext arrays in the |
| // spec. In the spec, it can store 3 possible values: 0, 1 and 2 (where 1 |
| // means the value is < 0, 2 means the value is > 0 and 0 means the value is |
| // equal to 0). |
| // |
| // The stored values are used in two places: |
| // * GetTransformAllZeroContext: Here, we only care about whether the |
| // value is 0 or not (whether it is 1 or 2 is irrelevant). |
| // * GetDcSignContext: Here, we do the following computation: if the |
| // stored value is 1, we decrement a counter. If the stored value is 2 |
| // we increment a counter. |
| // |
| // Based on this usage, we can simply replace 1 with -1 and 2 with 1 and |
| // use that value to compute the counter. |
| // |
| // The usage on GetTransformAllZeroContext is unaffected since there we |
| // only care about whether it is 0 or not. |
| std::array<Array2D<int8_t>, 2> dc_categories_; |
| const ObuSequenceHeader& sequence_header_; |
| const ObuFrameHeader& frame_header_; |
| const std::array<bool, kNumReferenceFrameTypes>& reference_frame_sign_bias_; |
| const std::array<RefCountedBufferPtr, kNumReferenceFrameTypes>& |
| reference_frames_; |
| TemporalMotionField& motion_field_; |
| const std::array<uint8_t, kNumReferenceFrameTypes>& reference_order_hint_; |
| const WedgeMaskArray& wedge_masks_; |
| DaalaBitReader reader_; |
| SymbolDecoderContext symbol_decoder_context_; |
| SymbolDecoderContext* const saved_symbol_decoder_context_; |
| const SegmentationMap* prev_segment_ids_; |
| const dsp::Dsp& dsp_; |
| PostFilter& post_filter_; |
| BlockParametersHolder& block_parameters_holder_; |
| Quantizer quantizer_; |
| // When there is no multi-threading within the Tile, |residual_buffer_| is |
| // used. When there is multi-threading within the Tile, |
| // |residual_buffer_threaded_| is used. In the following comment, |
| // |residual_buffer| refers to either |residual_buffer_| or |
| // |residual_buffer_threaded_| depending on whether multi-threading is enabled |
| // within the Tile or not. |
| // The |residual_buffer| is used to help with the dequantization and the |
| // inverse transform processes. It is declared as a uint8_t, but is always |
| // accessed either as an int16_t or int32_t depending on |bitdepth|. Here is |
| // what it stores at various stages of the decoding process (in the order |
| // which they happen): |
| // 1) In ReadTransformCoefficients(), this buffer is used to store the |
| // dequantized values. |
| // 2) In Reconstruct(), this buffer is used as the input to the row |
| // transform process. |
| // The size of this buffer would be: |
| // For |residual_buffer_|: (4096 + 32 * |kResidualPaddingVertical|) * |
| // |residual_size_|. Where 4096 = 64x64 which is the maximum transform |
| // size, and 32 * |kResidualPaddingVertical| is the padding to avoid |
| // bottom boundary checks when parsing quantized coefficients. This |
| // memory is allocated and owned by the Tile class. |
| // For |residual_buffer_threaded_|: See the comment below. This memory is |
| // not allocated or owned by the Tile class. |
| AlignedUniquePtr<uint8_t> residual_buffer_; |
| // This is a 2d array of pointers of size |superblock_rows_| by |
| // |superblock_columns_| where each pointer points to a ResidualBuffer for a |
| // single super block. The array is populated when the parsing process begins |
| // by calling |residual_buffer_pool_->Get()| and the memory is released back |
| // to the pool by calling |residual_buffer_pool_->Release()| when the decoding |
| // process is complete. |
| Array2D<std::unique_ptr<ResidualBuffer>> residual_buffer_threaded_; |
| // sizeof(int16_t or int32_t) depending on |bitdepth|. |
| const size_t residual_size_; |
| // Number of superblocks on the top-right that will have to be decoded before |
| // the current superblock can be decoded. This will be 1 if allow_intrabc is |
| // false. If allow_intrabc is true, then this value will be |
| // use_128x128_superblock ? 3 : 5. This is the allowed range of reference for |
| // the top rows for intrabc. |
| const int intra_block_copy_lag_; |
| |
| // In the Tile class, we use the "current_frame" in two ways: |
| // 1) To write the decoded output into (using the |buffer_| view). |
| // 2) To read the pixels for intra block copy (using the |current_frame_| |
| // reference). |
| // |
| // When intra block copy is off, |buffer_| and |current_frame_| may or may not |
| // point to the same plane pointers. But it is okay since |current_frame_| is |
| // never used in this case. |
| // |
| // When intra block copy is on, |buffer_| and |current_frame_| always point to |
| // the same plane pointers (since post filtering is disabled). So the usage in |
| // both case 1 and case 2 remain valid. |
| Array2DView<uint8_t> buffer_[kMaxPlanes]; |
| RefCountedBuffer& current_frame_; |
| |
| Array2D<int16_t>& cdef_index_; |
| Array2D<TransformSize>& inter_transform_sizes_; |
| std::array<RestorationUnitInfo, kMaxPlanes> reference_unit_info_; |
| // If |thread_pool_| is nullptr, the calling thread will do the parsing and |
| // the decoding in one pass. If |thread_pool_| is not nullptr, then the main |
| // thread will do the parsing while the thread pool workers will do the |
| // decoding. |
| ThreadPool* const thread_pool_; |
| ThreadingParameters threading_; |
| ResidualBufferPool* const residual_buffer_pool_; |
| TileScratchBufferPool* const tile_scratch_buffer_pool_; |
| BlockingCounterWithStatus* const pending_tiles_; |
| bool split_parse_and_decode_; |
| // This is used only when |split_parse_and_decode_| is false. |
| std::unique_ptr<PredictionParameters> prediction_parameters_ = nullptr; |
| // Stores the |transform_type| for the super block being decoded at a 4x4 |
| // granularity. The spec uses absolute indices for this array but it is |
| // sufficient to use indices relative to the super block being decoded. |
| TransformType transform_types_[32][32]; |
| // delta_lf_[i] is in the range [-63, 63]. |
| int8_t delta_lf_[kFrameLfCount]; |
| // True if all the values in |delta_lf_| are zero. False otherwise. |
| bool delta_lf_all_zero_; |
| const bool frame_parallel_; |
| const bool use_intra_prediction_buffer_; |
| // Buffer used to store the unfiltered pixels that are necessary for decoding |
| // the next superblock row (for the intra prediction process). Used only if |
| // |use_intra_prediction_buffer_| is true. The |frame_scratch_buffer| contains |
| // one row buffer for each tile row. This tile will have to use the buffer |
| // corresponding to this tile's row. |
| IntraPredictionBuffer* const intra_prediction_buffer_; |
| // Stores the progress of the reference frames. This will be used to avoid |
| // unnecessary calls into RefCountedBuffer::WaitUntil(). |
| std::array<int, kNumReferenceFrameTypes> reference_frame_progress_cache_; |
| }; |
| |
| struct Tile::Block { |
| Block(const Tile& tile, BlockSize size, int row4x4, int column4x4, |
| TileScratchBuffer* const scratch_buffer, ResidualPtr* residual) |
| : tile(tile), |
| size(size), |
| row4x4(row4x4), |
| column4x4(column4x4), |
| width(kBlockWidthPixels[size]), |
| height(kBlockHeightPixels[size]), |
| width4x4(width >> 2), |
| height4x4(height >> 2), |
| scratch_buffer(scratch_buffer), |
| residual(residual) { |
| assert(size != kBlockInvalid); |
| residual_size[kPlaneY] = kPlaneResidualSize[size][0][0]; |
| residual_size[kPlaneU] = residual_size[kPlaneV] = |
| kPlaneResidualSize[size][tile.subsampling_x_[kPlaneU]] |
| [tile.subsampling_y_[kPlaneU]]; |
| assert(residual_size[kPlaneY] != kBlockInvalid); |
| if (tile.PlaneCount() > 1) { |
| assert(residual_size[kPlaneU] != kBlockInvalid); |
| } |
| if ((row4x4 & 1) == 0 && |
| (tile.sequence_header_.color_config.subsampling_y & height4x4) == 1) { |
| has_chroma = false; |
| } else if ((column4x4 & 1) == 0 && |
| (tile.sequence_header_.color_config.subsampling_x & width4x4) == |
| 1) { |
| has_chroma = false; |
| } else { |
| has_chroma = !tile.sequence_header_.color_config.is_monochrome; |
| } |
| top_available[kPlaneY] = tile.IsTopInside(row4x4); |
| left_available[kPlaneY] = tile.IsLeftInside(column4x4); |
| if (has_chroma) { |
| // top_available[kPlaneU] and top_available[kPlaneV] are valid only if |
| // has_chroma is true. |
| // The next 3 lines are equivalent to: |
| // top_available[kPlaneU] = top_available[kPlaneV] = |
| // top_available[kPlaneY] && |
| // ((tile.sequence_header_.color_config.subsampling_y & height4x4) == |
| // 0 || tile.IsTopInside(row4x4 - 1)); |
| top_available[kPlaneU] = top_available[kPlaneV] = tile.IsTopInside( |
| row4x4 - |
| (tile.sequence_header_.color_config.subsampling_y & height4x4)); |
| // left_available[kPlaneU] and left_available[kPlaneV] are valid only if |
| // has_chroma is true. |
| // The next 3 lines are equivalent to: |
| // left_available[kPlaneU] = left_available[kPlaneV] = |
| // left_available[kPlaneY] && |
| // ((tile.sequence_header_.color_config.subsampling_x & width4x4) == 0 |
| // || tile.IsLeftInside(column4x4 - 1)); |
| left_available[kPlaneU] = left_available[kPlaneV] = tile.IsLeftInside( |
| column4x4 - |
| (tile.sequence_header_.color_config.subsampling_x & width4x4)); |
| } |
| const ptrdiff_t stride = tile.BlockParametersStride(); |
| BlockParameters** const bps = |
| tile.BlockParametersAddress(row4x4, column4x4); |
| bp = *bps; |
| // bp_top is valid only if top_available[kPlaneY] is true. |
| if (top_available[kPlaneY]) { |
| bp_top = *(bps - stride); |
| } |
| // bp_left is valid only if left_available[kPlaneY] is true. |
| if (left_available[kPlaneY]) { |
| bp_left = *(bps - 1); |
| } |
| } |
| |
| bool HasChroma() const { return has_chroma; } |
| |
| // These return values of these group of functions are valid only if the |
| // corresponding top_available or left_available is true. |
| ReferenceFrameType TopReference(int index) const { |
| return bp_top->reference_frame[index]; |
| } |
| |
| ReferenceFrameType LeftReference(int index) const { |
| return bp_left->reference_frame[index]; |
| } |
| |
| bool IsTopIntra() const { return TopReference(0) <= kReferenceFrameIntra; } |
| bool IsLeftIntra() const { return LeftReference(0) <= kReferenceFrameIntra; } |
| |
| bool IsTopSingle() const { return TopReference(1) <= kReferenceFrameIntra; } |
| bool IsLeftSingle() const { return LeftReference(1) <= kReferenceFrameIntra; } |
| |
| int CountReferences(ReferenceFrameType type) const { |
| return static_cast<int>(top_available[kPlaneY] && |
| bp_top->reference_frame[0] == type) + |
| static_cast<int>(top_available[kPlaneY] && |
| bp_top->reference_frame[1] == type) + |
| static_cast<int>(left_available[kPlaneY] && |
| bp_left->reference_frame[0] == type) + |
| static_cast<int>(left_available[kPlaneY] && |
| bp_left->reference_frame[1] == type); |
| } |
| |
| // 7.10.3. |
| // Checks if there are any inter blocks to the left or above. If so, it |
| // returns true indicating that the block has neighbors that are suitable for |
| // use by overlapped motion compensation. |
| bool HasOverlappableCandidates() const { |
| const ptrdiff_t stride = tile.BlockParametersStride(); |
| BlockParameters** const bps = tile.BlockParametersAddress(0, 0); |
| if (top_available[kPlaneY]) { |
| BlockParameters** bps_top = bps + (row4x4 - 1) * stride + (column4x4 | 1); |
| const int columns = std::min(tile.frame_header_.columns4x4 - column4x4, |
| static_cast<int>(width4x4)); |
| BlockParameters** const bps_top_end = bps_top + columns; |
| do { |
| if ((*bps_top)->reference_frame[0] > kReferenceFrameIntra) { |
| return true; |
| } |
| bps_top += 2; |
| } while (bps_top < bps_top_end); |
| } |
| if (left_available[kPlaneY]) { |
| BlockParameters** bps_left = bps + (row4x4 | 1) * stride + column4x4 - 1; |
| const int rows = std::min(tile.frame_header_.rows4x4 - row4x4, |
| static_cast<int>(height4x4)); |
| BlockParameters** const bps_left_end = bps_left + rows * stride; |
| do { |
| if ((*bps_left)->reference_frame[0] > kReferenceFrameIntra) { |
| return true; |
| } |
| bps_left += 2 * stride; |
| } while (bps_left < bps_left_end); |
| } |
| return false; |
| } |
| |
| const Tile& tile; |
| bool has_chroma; |
| const BlockSize size; |
| bool top_available[kMaxPlanes]; |
| bool left_available[kMaxPlanes]; |
| BlockSize residual_size[kMaxPlanes]; |
| const int row4x4; |
| const int column4x4; |
| const int width; |
| const int height; |
| const int width4x4; |
| const int height4x4; |
| const BlockParameters* bp_top; |
| const BlockParameters* bp_left; |
| BlockParameters* bp; |
| TileScratchBuffer* const scratch_buffer; |
| ResidualPtr* const residual; |
| }; |
| |
| extern template bool |
| Tile::ProcessSuperBlockRow<kProcessingModeDecodeOnly, false>( |
| int row4x4, TileScratchBuffer* scratch_buffer); |
| extern template bool |
| Tile::ProcessSuperBlockRow<kProcessingModeParseAndDecode, true>( |
| int row4x4, TileScratchBuffer* scratch_buffer); |
| |
| } // namespace libgav1 |
| |
| #endif // LIBGAV1_SRC_TILE_H_ |