1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 05:37:34 +00:00

LibVideo/VP9: Add Frame, Tile and Block context structs

These are used to pass context needed for decoding, with mutability
scoped only to the sections that the function receiving the contexts
needs to modify. This allows lifetimes of data to be more explicit
rather than being stored in fields, as well as preventing tile threads
from modifying outside their allowed bounds.
This commit is contained in:
Zaggy1024 2022-11-23 16:51:57 -06:00 committed by Andreas Kling
parent 448a8b8efb
commit befcd479ae
5 changed files with 423 additions and 355 deletions

View file

@ -12,9 +12,8 @@
#include <LibGfx/Size.h> #include <LibGfx/Size.h>
#include <LibVideo/Color/CodingIndependentCodePoints.h> #include <LibVideo/Color/CodingIndependentCodePoints.h>
#include <AK/Format.h>
#include "Enums.h" #include "Enums.h"
#include "LookupTables.h"
#include "MotionVector.h" #include "MotionVector.h"
namespace Video::VP9 { namespace Video::VP9 {
@ -153,7 +152,7 @@ public:
} }
template<typename OtherT, typename Function> template<typename OtherT, typename Function>
void copy_to(Vector2D<OtherT>& other, Function function) void copy_to(Vector2D<OtherT>& other, Function function) const
{ {
VERIFY(width() <= other.width()); VERIFY(width() <= other.width());
VERIFY(height() <= other.height()); VERIFY(height() <= other.height());
@ -163,7 +162,7 @@ public:
} }
} }
void copy_to(Vector2D<T>& other) void copy_to(Vector2D<T>& other) const
{ {
VERIFY(width() <= other.width()); VERIFY(width() <= other.width());
VERIFY(height() <= other.height()); VERIFY(height() <= other.height());
@ -238,4 +237,100 @@ struct PersistentBlockContext {
u8 segment_id { 0 }; u8 segment_id { 0 };
}; };
struct FrameContext {
public:
u8 profile { 0 };
bool shows_existing_frame() const { return m_show_existing_frame; }
u8 existing_frame_index() const { return m_existing_frame_index; }
void set_existing_frame_to_show(u8 index)
{
m_show_existing_frame = true;
m_existing_frame_index = index;
}
Gfx::Size<u32> size() const { return m_size; }
ErrorOr<void> set_size(Gfx::Size<u32> size)
{
m_size = size;
// From spec, compute_image_size( )
m_rows = (size.height() + 7u) >> 3u;
m_columns = (size.width() + 7u) >> 3u;
return m_block_contexts.try_resize(m_rows, m_columns);
}
u32 rows() const { return m_rows; }
u32 columns() const { return m_columns; }
u32 superblock_rows() const { return (rows() + 7u) >> 3u; }
u32 superblock_columns() const { return (columns() + 7u) >> 3u; }
Vector2D<FrameBlockContext> const& block_contexts() const { return m_block_contexts; }
Gfx::Size<u32> render_size { 0, 0 };
u16 header_size_in_bytes { 0 };
private:
friend struct TileContext;
bool m_show_existing_frame { false };
u8 m_existing_frame_index { 0 };
Gfx::Size<u32> m_size { 0, 0 };
u32 m_rows { 0 };
u32 m_columns { 0 };
// FIXME: From spec: NOTE We are using a 2D array to store the SubModes for clarity. It is possible to reduce memory
// consumption by only storing one intra mode for each 8x8 horizontal and vertical position, i.e. to use two 1D
// arrays instead.
// I think should also apply to other fields that are only accessed relative to the current block. Worth looking
// into how much of this context needs to be stored for the whole frame vs a row or column from the current tile.
Vector2D<FrameBlockContext> m_block_contexts;
};
struct TileContext {
public:
TileContext(FrameContext& frame_context, u32 rows_start, u32 rows_end, u32 columns_start, u32 columns_end)
: frame_context(frame_context)
, rows_start(rows_start)
, rows_end(rows_end)
, columns_start(columns_start)
, columns_end(columns_end)
, block_contexts_view(frame_context.m_block_contexts.view(rows_start, columns_start, rows_end - rows_start, columns_end - columns_start))
{
}
Vector2D<FrameBlockContext> const& frame_block_contexts() const { return frame_context.block_contexts(); }
FrameContext const& frame_context;
u32 rows_start { 0 };
u32 rows_end { 0 };
u32 columns_start { 0 };
u32 columns_end { 0 };
Vector2DView<FrameBlockContext> block_contexts_view;
};
struct BlockContext {
BlockContext(TileContext& tile_context, u32 row, u32 column, BlockSubsize size)
: frame_context(tile_context.frame_context)
, tile_context(tile_context)
, row(row)
, column(column)
, size(size)
, contexts_view(tile_context.block_contexts_view.view(row - tile_context.rows_start, column - tile_context.columns_start,
min<u32>(num_8x8_blocks_high_lookup[size], tile_context.frame_context.rows() - row),
min<u32>(num_8x8_blocks_wide_lookup[size], tile_context.frame_context.columns() - column)))
{
}
Vector2D<FrameBlockContext> const& frame_block_contexts() const { return frame_context.block_contexts(); }
FrameContext const& frame_context;
TileContext const& tile_context;
u32 row { 0 };
u32 column { 0 };
BlockSubsize size;
Vector2DView<FrameBlockContext> contexts_view;
};
} }

View file

@ -55,7 +55,7 @@ DecoderErrorOr<void> Decoder::decode_frame(ReadonlyBytes frame_data)
{ {
// 1. The syntax elements for the coded frame are extracted as specified in sections 6 and 7. The syntax // 1. The syntax elements for the coded frame are extracted as specified in sections 6 and 7. The syntax
// tables include function calls indicating when the block decode processes should be triggered. // tables include function calls indicating when the block decode processes should be triggered.
TRY(m_parser->parse_frame(frame_data)); auto frame_context = TRY(m_parser->parse_frame(frame_data));
// 2. If loop_filter_level is not equal to 0, the loop filter process as specified in section 8.8 is invoked once the // 2. If loop_filter_level is not equal to 0, the loop filter process as specified in section 8.8 is invoked once the
// coded frame has been decoded. // coded frame has been decoded.
@ -70,17 +70,17 @@ DecoderErrorOr<void> Decoder::decode_frame(ReadonlyBytes frame_data)
// 4. The output process as specified in section 8.9 is invoked. // 4. The output process as specified in section 8.9 is invoked.
if (m_parser->m_show_frame) if (m_parser->m_show_frame)
TRY(create_video_frame()); TRY(create_video_frame(frame_context));
// 5. The reference frame update process as specified in section 8.10 is invoked. // 5. The reference frame update process as specified in section 8.10 is invoked.
TRY(update_reference_frames()); TRY(update_reference_frames(frame_context));
return {}; return {};
} }
DecoderErrorOr<void> Decoder::create_video_frame() DecoderErrorOr<void> Decoder::create_video_frame(FrameContext const& frame_context)
{ {
u32 decoded_y_width = m_parser->m_mi_cols * 8; u32 decoded_y_width = frame_context.columns() * 8;
Gfx::Size<u32> output_y_size = m_parser->m_frame_size; Gfx::Size<u32> output_y_size = frame_context.size();
auto decoded_uv_width = decoded_y_width >> m_parser->m_subsampling_x; auto decoded_uv_width = decoded_y_width >> m_parser->m_subsampling_x;
Gfx::Size<u32> output_uv_size = { Gfx::Size<u32> output_uv_size = {
output_y_size.width() >> m_parser->m_subsampling_x, output_y_size.width() >> m_parser->m_subsampling_x,
@ -125,10 +125,10 @@ inline size_t buffer_size(Gfx::Size<size_t> size)
return buffer_size(size.width(), size.height()); return buffer_size(size.width(), size.height());
} }
DecoderErrorOr<void> Decoder::allocate_buffers() DecoderErrorOr<void> Decoder::allocate_buffers(FrameContext const& frame_context)
{ {
for (size_t plane = 0; plane < 3; plane++) { for (size_t plane = 0; plane < 3; plane++) {
auto size = m_parser->get_decoded_size_for_plane(plane); auto size = m_parser->get_decoded_size_for_plane(frame_context, plane);
auto& output_buffer = get_output_buffer(plane); auto& output_buffer = get_output_buffer(plane);
output_buffer.clear_with_capacity(); output_buffer.clear_with_capacity();
@ -329,7 +329,7 @@ u8 Decoder::adapt_prob(u8 prob, u8 counts[2])
return merge_prob(prob, counts[0], counts[1], COUNT_SAT, MAX_UPDATE_FACTOR); return merge_prob(prob, counts[0], counts[1], COUNT_SAT, MAX_UPDATE_FACTOR);
} }
DecoderErrorOr<void> Decoder::predict_intra(u8 plane, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TXSize tx_size, u32 block_index) DecoderErrorOr<void> Decoder::predict_intra(u8 plane, BlockContext const& block_context, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TXSize tx_size, u32 block_index)
{ {
auto& frame_buffer = get_output_buffer(plane); auto& frame_buffer = get_output_buffer(plane);
@ -346,7 +346,7 @@ DecoderErrorOr<void> Decoder::predict_intra(u8 plane, u32 x, u32 y, bool have_le
PredictionMode mode; PredictionMode mode;
if (plane > 0) if (plane > 0)
mode = m_parser->m_uv_mode; mode = m_parser->m_uv_mode;
else if (m_parser->m_mi_size >= Block_8x8) else if (block_context.size >= Block_8x8)
mode = m_parser->m_y_mode; mode = m_parser->m_y_mode;
else else
mode = m_parser->m_block_sub_modes[block_index]; mode = m_parser->m_block_sub_modes[block_index];
@ -363,8 +363,8 @@ DecoderErrorOr<void> Decoder::predict_intra(u8 plane, u32 x, u32 y, bool have_le
// maxY is set equal to ((MiRows * 8) >> subsampling_y) - 1. // maxY is set equal to ((MiRows * 8) >> subsampling_y) - 1.
auto subsampling_x = plane > 0 ? m_parser->m_subsampling_x : false; auto subsampling_x = plane > 0 ? m_parser->m_subsampling_x : false;
auto subsampling_y = plane > 0 ? m_parser->m_subsampling_y : false; auto subsampling_y = plane > 0 ? m_parser->m_subsampling_y : false;
auto max_x = ((m_parser->m_mi_cols * 8u) >> subsampling_x) - 1u; auto max_x = ((block_context.frame_context.columns() * 8u) >> subsampling_x) - 1u;
auto max_y = ((m_parser->m_mi_rows * 8u) >> subsampling_y) - 1u; auto max_y = ((block_context.frame_context.rows() * 8u) >> subsampling_y) - 1u;
auto const frame_buffer_at = [&](u32 row, u32 column) -> u16& { auto const frame_buffer_at = [&](u32 row, u32 column) -> u16& {
const auto frame_stride = max_x + 1u; const auto frame_stride = max_x + 1u;
@ -665,7 +665,7 @@ DecoderErrorOr<void> Decoder::predict_intra(u8 plane, u32 x, u32 y, bool have_le
return {}; return {};
} }
MotionVector Decoder::select_motion_vector(u8 plane, u8 ref_list, u32 block_index) MotionVector Decoder::select_motion_vector(u8 plane, BlockContext const& block_context, u8 ref_list, u32 block_index)
{ {
// The inputs to this process are: // The inputs to this process are:
// a variable plane specifying which plane is being predicted, // a variable plane specifying which plane is being predicted,
@ -697,7 +697,7 @@ MotionVector Decoder::select_motion_vector(u8 plane, u8 ref_list, u32 block_inde
// The motion vector array mv is derived as follows: // The motion vector array mv is derived as follows:
// If plane is equal to 0, or MiSize is greater than or equal to BLOCK_8X8, mv is set equal to // If plane is equal to 0, or MiSize is greater than or equal to BLOCK_8X8, mv is set equal to
// BlockMvs[ refList ][ blockIdx ]. // BlockMvs[ refList ][ blockIdx ].
if (plane == 0 || m_parser->m_mi_size >= Block_8x8) if (plane == 0 || block_context.size >= Block_8x8)
return m_parser->m_block_mvs[ref_list][block_index]; return m_parser->m_block_mvs[ref_list][block_index];
// Otherwise, if subsampling_x is equal to 0 and subsampling_y is equal to 0, mv is set equal to // Otherwise, if subsampling_x is equal to 0 and subsampling_y is equal to 0, mv is set equal to
// BlockMvs[ refList ][ blockIdx ]. // BlockMvs[ refList ][ blockIdx ].
@ -721,7 +721,7 @@ MotionVector Decoder::select_motion_vector(u8 plane, u8 ref_list, u32 block_inde
+ m_parser->m_block_mvs[ref_list][2] + m_parser->m_block_mvs[ref_list][3]); + m_parser->m_block_mvs[ref_list][2] + m_parser->m_block_mvs[ref_list][3]);
} }
MotionVector Decoder::clamp_motion_vector(u8 plane, u32 block_row, u32 block_column, MotionVector vector) MotionVector Decoder::clamp_motion_vector(u8 plane, BlockContext const& block_context, u32 block_row, u32 block_column, MotionVector vector)
{ {
// FIXME: This function is named very similarly to Parser::clamp_mv. Rename one or the other? // FIXME: This function is named very similarly to Parser::clamp_mv. Rename one or the other?
@ -734,14 +734,14 @@ MotionVector Decoder::clamp_motion_vector(u8 plane, u32 block_row, u32 block_col
bool subsampling_y = plane > 0 ? m_parser->m_subsampling_y : false; bool subsampling_y = plane > 0 ? m_parser->m_subsampling_y : false;
// The output array clampedMv is specified by the following steps: // The output array clampedMv is specified by the following steps:
i32 blocks_high = num_8x8_blocks_high_lookup[m_parser->m_mi_size]; i32 blocks_high = num_8x8_blocks_high_lookup[block_context.size];
// Casts must be done here to prevent subtraction underflow from wrapping the values. // Casts must be done here to prevent subtraction underflow from wrapping the values.
i32 mb_to_top_edge = -(static_cast<i32>(block_row * MI_SIZE) * 16) >> subsampling_y; i32 mb_to_top_edge = -(static_cast<i32>(block_row * MI_SIZE) * 16) >> subsampling_y;
i32 mb_to_bottom_edge = (((static_cast<i32>(m_parser->m_mi_rows) - blocks_high - static_cast<i32>(block_row)) * MI_SIZE) * 16) >> subsampling_y; i32 mb_to_bottom_edge = (((static_cast<i32>(block_context.frame_context.rows()) - blocks_high - static_cast<i32>(block_row)) * MI_SIZE) * 16) >> subsampling_y;
i32 blocks_wide = num_8x8_blocks_wide_lookup[m_parser->m_mi_size]; i32 blocks_wide = num_8x8_blocks_wide_lookup[block_context.size];
i32 mb_to_left_edge = -(static_cast<i32>(block_column * MI_SIZE) * 16) >> subsampling_x; i32 mb_to_left_edge = -(static_cast<i32>(block_column * MI_SIZE) * 16) >> subsampling_x;
i32 mb_to_right_edge = (((static_cast<i32>(m_parser->m_mi_cols) - blocks_wide - static_cast<i32>(block_column)) * MI_SIZE) * 16) >> subsampling_x; i32 mb_to_right_edge = (((static_cast<i32>(block_context.frame_context.columns()) - blocks_wide - static_cast<i32>(block_column)) * MI_SIZE) * 16) >> subsampling_x;
i32 subpel_left = (INTERP_EXTEND + ((blocks_wide * MI_SIZE) >> subsampling_x)) << SUBPEL_BITS; i32 subpel_left = (INTERP_EXTEND + ((blocks_wide * MI_SIZE) >> subsampling_x)) << SUBPEL_BITS;
i32 subpel_right = subpel_left - SUBPEL_SHIFTS; i32 subpel_right = subpel_left - SUBPEL_SHIFTS;
@ -753,16 +753,16 @@ MotionVector Decoder::clamp_motion_vector(u8 plane, u32 block_row, u32 block_col
}; };
} }
DecoderErrorOr<void> Decoder::predict_inter_block(u8 plane, u8 ref_list, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index, Span<u16> block_buffer) DecoderErrorOr<void> Decoder::predict_inter_block(u8 plane, BlockContext const& block_context, u8 ref_list, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index, Span<u16> block_buffer)
{ {
VERIFY(width <= maximum_block_dimensions && height <= maximum_block_dimensions); VERIFY(width <= maximum_block_dimensions && height <= maximum_block_dimensions);
// 2. The motion vector selection process in section 8.5.2.1 is invoked with plane, refList, blockIdx as inputs // 2. The motion vector selection process in section 8.5.2.1 is invoked with plane, refList, blockIdx as inputs
// and the output being the motion vector mv. // and the output being the motion vector mv.
auto motion_vector = select_motion_vector(plane, ref_list, block_index); auto motion_vector = select_motion_vector(plane, block_context, ref_list, block_index);
// 3. The motion vector clamping process in section 8.5.2.2 is invoked with plane, mv as inputs and the output // 3. The motion vector clamping process in section 8.5.2.2 is invoked with plane, mv as inputs and the output
// being the clamped motion vector clampedMv // being the clamped motion vector clampedMv
auto clamped_vector = clamp_motion_vector(plane, block_row, block_column, motion_vector); auto clamped_vector = clamp_motion_vector(plane, block_context, block_row, block_column, motion_vector);
// 4. The motion vector scaling process in section 8.5.2.3 is invoked with plane, refList, x, y, clampedMv as // 4. The motion vector scaling process in section 8.5.2.3 is invoked with plane, refList, x, y, clampedMv as
// inputs and the output being the initial location startX, startY, and the step sizes stepX, stepY. // inputs and the output being the initial location startX, startY, and the step sizes stepX, stepY.
@ -791,10 +791,10 @@ DecoderErrorOr<void> Decoder::predict_inter_block(u8 plane, u8 ref_list, u32 blo
if (m_parser->m_frame_store[reference_frame_index][plane].is_empty()) if (m_parser->m_frame_store[reference_frame_index][plane].is_empty())
return DecoderError::format(DecoderErrorCategory::Corrupted, "Attempted to use reference frame {} that has not been saved", reference_frame_index); return DecoderError::format(DecoderErrorCategory::Corrupted, "Attempted to use reference frame {} that has not been saved", reference_frame_index);
auto ref_frame_size = m_parser->m_ref_frame_size[reference_frame_index]; auto ref_frame_size = m_parser->m_ref_frame_size[reference_frame_index];
auto double_frame_size = m_parser->m_frame_size.scaled_by(2); auto double_frame_size = block_context.frame_context.size().scaled_by(2);
if (double_frame_size.width() < ref_frame_size.width() || double_frame_size.height() < ref_frame_size.height()) if (double_frame_size.width() < ref_frame_size.width() || double_frame_size.height() < ref_frame_size.height())
return DecoderError::format(DecoderErrorCategory::Corrupted, "Inter frame size is too small relative to reference frame {}", reference_frame_index); return DecoderError::format(DecoderErrorCategory::Corrupted, "Inter frame size is too small relative to reference frame {}", reference_frame_index);
if (!ref_frame_size.scaled_by(16).contains(m_parser->m_frame_size)) if (!ref_frame_size.scaled_by(16).contains(block_context.frame_context.size()))
return DecoderError::format(DecoderErrorCategory::Corrupted, "Inter frame size is too large relative to reference frame {}", reference_frame_index); return DecoderError::format(DecoderErrorCategory::Corrupted, "Inter frame size is too large relative to reference frame {}", reference_frame_index);
// FIXME: Convert all the operations in this function to vector operations supported by // FIXME: Convert all the operations in this function to vector operations supported by
@ -804,8 +804,8 @@ DecoderErrorOr<void> Decoder::predict_inter_block(u8 plane, u8 ref_list, u32 blo
// A variable yScale is set equal to (RefFrameHeight[ refIdx ] << REF_SCALE_SHIFT) / FrameHeight. // A variable yScale is set equal to (RefFrameHeight[ refIdx ] << REF_SCALE_SHIFT) / FrameHeight.
// (xScale and yScale specify the size of the reference frame relative to the current frame in units where 16 is // (xScale and yScale specify the size of the reference frame relative to the current frame in units where 16 is
// equivalent to the reference frame having the same size.) // equivalent to the reference frame having the same size.)
i32 x_scale = (ref_frame_size.width() << REF_SCALE_SHIFT) / m_parser->m_frame_size.width(); i32 x_scale = (ref_frame_size.width() << REF_SCALE_SHIFT) / block_context.frame_context.size().width();
i32 y_scale = (ref_frame_size.height() << REF_SCALE_SHIFT) / m_parser->m_frame_size.height(); i32 y_scale = (ref_frame_size.height() << REF_SCALE_SHIFT) / block_context.frame_context.size().height();
// The variable baseX is set equal to (x * xScale) >> REF_SCALE_SHIFT. // The variable baseX is set equal to (x * xScale) >> REF_SCALE_SHIFT.
// The variable baseY is set equal to (y * yScale) >> REF_SCALE_SHIFT. // The variable baseY is set equal to (y * yScale) >> REF_SCALE_SHIFT.
@ -923,7 +923,7 @@ DecoderErrorOr<void> Decoder::predict_inter_block(u8 plane, u8 ref_list, u32 blo
return {}; return {};
} }
DecoderErrorOr<void> Decoder::predict_inter(u8 plane, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index) DecoderErrorOr<void> Decoder::predict_inter(u8 plane, BlockContext const& block_context, u32 x, u32 y, u32 width, u32 height, u32 block_index)
{ {
// The inter prediction process is invoked for inter coded blocks. When MiSize is smaller than BLOCK_8X8, the // The inter prediction process is invoked for inter coded blocks. When MiSize is smaller than BLOCK_8X8, the
// prediction is done with a granularity of 4x4 samples, otherwise the whole plane is predicted at the same time. // prediction is done with a granularity of 4x4 samples, otherwise the whole plane is predicted at the same time.
@ -942,7 +942,7 @@ DecoderErrorOr<void> Decoder::predict_inter(u8 plane, u32 block_row, u32 block_c
// 2. through 5. // 2. through 5.
Array<u16, maximum_block_size> predicted_buffer; Array<u16, maximum_block_size> predicted_buffer;
auto predicted_span = predicted_buffer.span().trim(width * height); auto predicted_span = predicted_buffer.span().trim(width * height);
TRY(predict_inter_block(plane, 0, block_row, block_column, x, y, width, height, block_index, predicted_span)); TRY(predict_inter_block(plane, block_context, 0, block_context.row, block_context.column, x, y, width, height, block_index, predicted_span));
auto predicted_buffer_at = [&](Span<u16> buffer, u32 row, u32 column) -> u16& { auto predicted_buffer_at = [&](Span<u16> buffer, u32 row, u32 column) -> u16& {
return buffer[row * width + column]; return buffer[row * width + column];
}; };
@ -952,8 +952,8 @@ DecoderErrorOr<void> Decoder::predict_inter(u8 plane, u32 block_row, u32 block_c
// The inter predicted samples are then derived as follows: // The inter predicted samples are then derived as follows:
auto& frame_buffer = get_output_buffer(plane); auto& frame_buffer = get_output_buffer(plane);
VERIFY(!frame_buffer.is_empty()); VERIFY(!frame_buffer.is_empty());
auto frame_width = (m_parser->m_mi_cols * 8u) >> (plane > 0 ? m_parser->m_subsampling_x : false); auto frame_width = (block_context.frame_context.columns() * 8u) >> (plane > 0 ? m_parser->m_subsampling_x : false);
auto frame_height = (m_parser->m_mi_rows * 8u) >> (plane > 0 ? m_parser->m_subsampling_y : false); auto frame_height = (block_context.frame_context.rows() * 8u) >> (plane > 0 ? m_parser->m_subsampling_y : false);
auto frame_buffer_at = [&](u32 row, u32 column) -> u16& { auto frame_buffer_at = [&](u32 row, u32 column) -> u16& {
return frame_buffer[row * frame_width + column]; return frame_buffer[row * frame_width + column];
}; };
@ -976,7 +976,7 @@ DecoderErrorOr<void> Decoder::predict_inter(u8 plane, u32 block_row, u32 block_c
// for i = 0..h-1 and j = 0..w-1. // for i = 0..h-1 and j = 0..w-1.
Array<u16, maximum_block_size> second_predicted_buffer; Array<u16, maximum_block_size> second_predicted_buffer;
auto second_predicted_span = second_predicted_buffer.span().trim(width * height); auto second_predicted_span = second_predicted_buffer.span().trim(width * height);
TRY(predict_inter_block(plane, 1, block_row, block_column, x, y, width, height, block_index, second_predicted_span)); TRY(predict_inter_block(plane, block_context, 1, block_context.row, block_context.column, x, y, width, height, block_index, second_predicted_span));
for (auto i = 0u; i < height_in_frame_buffer; i++) { for (auto i = 0u; i < height_in_frame_buffer; i++) {
for (auto j = 0u; j < width_in_frame_buffer; j++) for (auto j = 0u; j < width_in_frame_buffer; j++)
@ -1055,7 +1055,7 @@ u16 Decoder::get_ac_quant(u8 plane)
return ac_q(static_cast<u8>(get_qindex() + offset)); return ac_q(static_cast<u8>(get_qindex() + offset));
} }
DecoderErrorOr<void> Decoder::reconstruct(u8 plane, u32 transform_block_x, u32 transform_block_y, TXSize transform_block_size) DecoderErrorOr<void> Decoder::reconstruct(u8 plane, BlockContext const& block_context, u32 transform_block_x, u32 transform_block_y, TXSize transform_block_size)
{ {
// 8.6.2 Reconstruct process // 8.6.2 Reconstruct process
@ -1096,8 +1096,8 @@ DecoderErrorOr<void> Decoder::reconstruct(u8 plane, u32 transform_block_x, u32 t
auto& current_buffer = get_output_buffer(plane); auto& current_buffer = get_output_buffer(plane);
auto subsampling_x = (plane > 0 ? m_parser->m_subsampling_x : 0); auto subsampling_x = (plane > 0 ? m_parser->m_subsampling_x : 0);
auto subsampling_y = (plane > 0 ? m_parser->m_subsampling_y : 0); auto subsampling_y = (plane > 0 ? m_parser->m_subsampling_y : 0);
auto frame_width = (m_parser->m_mi_cols * 8) >> subsampling_x; auto frame_width = (block_context.frame_context.columns() * 8) >> subsampling_x;
auto frame_height = (m_parser->m_mi_rows * 8) >> subsampling_y; auto frame_height = (block_context.frame_context.rows() * 8) >> subsampling_y;
auto width_in_frame_buffer = min(block_size, frame_width - transform_block_x); auto width_in_frame_buffer = min(block_size, frame_width - transform_block_x);
auto height_in_frame_buffer = min(block_size, frame_height - transform_block_y); auto height_in_frame_buffer = min(block_size, frame_height - transform_block_y);
@ -1742,7 +1742,7 @@ DecoderErrorOr<void> Decoder::inverse_transform_2d(Span<Intermediate> dequantize
return {}; return {};
} }
DecoderErrorOr<void> Decoder::update_reference_frames() DecoderErrorOr<void> Decoder::update_reference_frames(FrameContext const& frame_context)
{ {
// This process is invoked as the final step in decoding a frame. // This process is invoked as the final step in decoding a frame.
// The inputs to this process are the samples in the current frame CurrFrame[ plane ][ x ][ y ]. // The inputs to this process are the samples in the current frame CurrFrame[ plane ][ x ][ y ].
@ -1756,7 +1756,7 @@ DecoderErrorOr<void> Decoder::update_reference_frames()
if ((refresh_flags & 1) != 0) { if ((refresh_flags & 1) != 0) {
// RefFrameWidth[ i ] is set equal to FrameWidth. // RefFrameWidth[ i ] is set equal to FrameWidth.
// RefFrameHeight[ i ] is set equal to FrameHeight. // RefFrameHeight[ i ] is set equal to FrameHeight.
m_parser->m_ref_frame_size[i] = m_parser->m_frame_size; m_parser->m_ref_frame_size[i] = frame_context.size();
// RefSubsamplingX[ i ] is set equal to subsampling_x. // RefSubsamplingX[ i ] is set equal to subsampling_x.
m_parser->m_ref_subsampling_x[i] = m_parser->m_subsampling_x; m_parser->m_ref_subsampling_x[i] = m_parser->m_subsampling_x;
// RefSubsamplingY[ i ] is set equal to subsampling_y. // RefSubsamplingY[ i ] is set equal to subsampling_y.
@ -1773,9 +1773,9 @@ DecoderErrorOr<void> Decoder::update_reference_frames()
// FIXME: Frame width is not equal to the buffer's stride. If we store the stride of the buffer with the reference // FIXME: Frame width is not equal to the buffer's stride. If we store the stride of the buffer with the reference
// frame, we can just copy the framebuffer data instead. Alternatively, we should crop the output framebuffer. // frame, we can just copy the framebuffer data instead. Alternatively, we should crop the output framebuffer.
for (auto plane = 0u; plane < 3; plane++) { for (auto plane = 0u; plane < 3; plane++) {
auto width = m_parser->m_frame_size.width(); auto width = frame_context.size().width();
auto height = m_parser->m_frame_size.height(); auto height = frame_context.size().height();
auto stride = m_parser->m_mi_cols * 8; auto stride = frame_context.columns() * 8;
if (plane > 0) { if (plane > 0) {
width = (width + m_parser->m_subsampling_x) >> m_parser->m_subsampling_x; width = (width + m_parser->m_subsampling_x) >> m_parser->m_subsampling_x;
@ -1800,8 +1800,8 @@ DecoderErrorOr<void> Decoder::update_reference_frames()
} }
// 2. If show_existing_frame is equal to 0, the following applies: // 2. If show_existing_frame is equal to 0, the following applies:
if (!m_parser->m_show_existing_frame) { if (!frame_context.shows_existing_frame()) {
DECODER_TRY_ALLOC(m_parser->m_previous_block_contexts.try_resize_to_match_other_vector2d(m_parser->m_frame_block_contexts)); DECODER_TRY_ALLOC(m_parser->m_previous_block_contexts.try_resize_to_match_other_vector2d(frame_context.block_contexts()));
// PrevRefFrames[ row ][ col ][ list ] is set equal to RefFrames[ row ][ col ][ list ] for row = 0..MiRows-1, // PrevRefFrames[ row ][ col ][ list ] is set equal to RefFrames[ row ][ col ][ list ] for row = 0..MiRows-1,
// for col = 0..MiCols-1, for list = 0..1. // for col = 0..MiCols-1, for list = 0..1.
// PrevMvs[ row ][ col ][ list ][ comp ] is set equal to Mvs[ row ][ col ][ list ][ comp ] for row = 0..MiRows-1, // PrevMvs[ row ][ col ][ list ][ comp ] is set equal to Mvs[ row ][ col ][ list ][ comp ] for row = 0..MiRows-1,
@ -1812,8 +1812,8 @@ DecoderErrorOr<void> Decoder::update_reference_frames()
// show_existing_frame is equal to 0, // show_existing_frame is equal to 0,
// segmentation_enabled is equal to 1, // segmentation_enabled is equal to 1,
// segmentation_update_map is equal to 1. // segmentation_update_map is equal to 1.
bool keep_segment_ids = !m_parser->m_show_existing_frame && m_parser->m_segmentation_enabled && m_parser->m_segmentation_update_map; bool keep_segment_ids = !frame_context.shows_existing_frame() && m_parser->m_segmentation_enabled && m_parser->m_segmentation_update_map;
m_parser->m_frame_block_contexts.copy_to(m_parser->m_previous_block_contexts, [keep_segment_ids](FrameBlockContext context) { frame_context.block_contexts().copy_to(m_parser->m_previous_block_contexts, [keep_segment_ids](FrameBlockContext context) {
auto persistent_context = PersistentBlockContext(context); auto persistent_context = PersistentBlockContext(context);
if (!keep_segment_ids) if (!keep_segment_ids)
persistent_context.segment_id = 0; persistent_context.segment_id = 0;

View file

@ -42,9 +42,9 @@ private:
static constexpr size_t maximum_transform_size = 32ULL * 32ULL; static constexpr size_t maximum_transform_size = 32ULL * 32ULL;
DecoderErrorOr<void> decode_frame(ReadonlyBytes); DecoderErrorOr<void> decode_frame(ReadonlyBytes);
DecoderErrorOr<void> create_video_frame(); DecoderErrorOr<void> create_video_frame(FrameContext const&);
DecoderErrorOr<void> allocate_buffers(); DecoderErrorOr<void> allocate_buffers(FrameContext const&);
Vector<Intermediate>& get_temp_buffer(u8 plane); Vector<Intermediate>& get_temp_buffer(u8 plane);
Vector<u16>& get_output_buffer(u8 plane); Vector<u16>& get_output_buffer(u8 plane);
@ -58,18 +58,18 @@ private:
/* (8.5) Prediction Processes */ /* (8.5) Prediction Processes */
// (8.5.1) Intra prediction process // (8.5.1) Intra prediction process
DecoderErrorOr<void> predict_intra(u8 plane, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TXSize tx_size, u32 block_index); DecoderErrorOr<void> predict_intra(u8 plane, BlockContext const& block_context, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TXSize tx_size, u32 block_index);
// (8.5.1) Inter prediction process // (8.5.1) Inter prediction process
DecoderErrorOr<void> predict_inter(u8 plane, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index); DecoderErrorOr<void> predict_inter(u8 plane, BlockContext const& block_context, u32 x, u32 y, u32 width, u32 height, u32 block_index);
// (8.5.2.1) Motion vector selection process // (8.5.2.1) Motion vector selection process
MotionVector select_motion_vector(u8 plane, u8 ref_list, u32 block_index); MotionVector select_motion_vector(u8 plane, BlockContext const&, u8 ref_list, u32 block_index);
// (8.5.2.2) Motion vector clamping process // (8.5.2.2) Motion vector clamping process
MotionVector clamp_motion_vector(u8 plane, u32 block_row, u32 block_column, MotionVector vector); MotionVector clamp_motion_vector(u8 plane, BlockContext const&, u32 block_row, u32 block_column, MotionVector vector);
// (8.5.2.3) Motion vector scaling process // (8.5.2.3) Motion vector scaling process
DecoderErrorOr<MotionVector> scale_motion_vector(u8 plane, u8 ref_list, u32 x, u32 y, MotionVector vector); DecoderErrorOr<MotionVector> scale_motion_vector(u8 plane, u8 ref_list, u32 x, u32 y, MotionVector vector);
// From (8.5.1) Inter prediction process, steps 2-5 // From (8.5.1) Inter prediction process, steps 2-5
DecoderErrorOr<void> predict_inter_block(u8 plane, u8 ref_list, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index, Span<u16> block_buffer); DecoderErrorOr<void> predict_inter_block(u8 plane, BlockContext const&, u8 ref_list, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index, Span<u16> block_buffer);
/* (8.6) Reconstruction and Dequantization */ /* (8.6) Reconstruction and Dequantization */
@ -84,7 +84,7 @@ private:
u16 get_ac_quant(u8 plane); u16 get_ac_quant(u8 plane);
// (8.6.2) Reconstruct process // (8.6.2) Reconstruct process
DecoderErrorOr<void> reconstruct(u8 plane, u32 transform_block_x, u32 transform_block_y, TXSize transform_block_size); DecoderErrorOr<void> reconstruct(u8 plane, BlockContext const&, u32 transform_block_x, u32 transform_block_y, TXSize transform_block_size);
// (8.7) Inverse transform process // (8.7) Inverse transform process
DecoderErrorOr<void> inverse_transform_2d(Span<Intermediate> dequantized, u8 log2_of_block_size); DecoderErrorOr<void> inverse_transform_2d(Span<Intermediate> dequantized, u8 log2_of_block_size);
@ -140,7 +140,7 @@ private:
inline DecoderErrorOr<void> inverse_asymmetric_discrete_sine_transform(Span<Intermediate> data, u8 log2_of_block_size); inline DecoderErrorOr<void> inverse_asymmetric_discrete_sine_transform(Span<Intermediate> data, u8 log2_of_block_size);
/* (8.10) Reference Frame Update Process */ /* (8.10) Reference Frame Update Process */
DecoderErrorOr<void> update_reference_frames(); DecoderErrorOr<void> update_reference_frames(FrameContext const&);
inline CodingIndependentCodePoints get_cicp_color_space(); inline CodingIndependentCodePoints get_cicp_color_space();

View file

@ -79,30 +79,34 @@ Vector<size_t> Parser::parse_superframe_sizes(ReadonlyBytes frame_data)
} }
/* (6.1) */ /* (6.1) */
DecoderErrorOr<void> Parser::parse_frame(ReadonlyBytes frame_data) DecoderErrorOr<FrameContext> Parser::parse_frame(ReadonlyBytes frame_data)
{ {
m_bit_stream = make<BitStream>(frame_data.data(), frame_data.size()); m_bit_stream = make<BitStream>(frame_data.data(), frame_data.size());
m_syntax_element_counter = make<SyntaxElementCounter>(); m_syntax_element_counter = make<SyntaxElementCounter>();
TRY(uncompressed_header()); auto frame_context = TRY(uncompressed_header());
if (!trailing_bits()) if (!trailing_bits())
return DecoderError::corrupted("Trailing bits were non-zero"sv); return DecoderError::corrupted("Trailing bits were non-zero"sv);
if (m_header_size_in_bytes == 0) // FIXME: This should not be an error. Spec says that we consume padding bits until the end of the sample.
if (frame_context.header_size_in_bytes == 0)
return DecoderError::corrupted("Frame header is zero-sized"sv); return DecoderError::corrupted("Frame header is zero-sized"sv);
m_probability_tables->load_probs(m_frame_context_idx); m_probability_tables->load_probs(m_frame_context_idx);
m_probability_tables->load_probs2(m_frame_context_idx); m_probability_tables->load_probs2(m_frame_context_idx);
m_syntax_element_counter->clear_counts(); m_syntax_element_counter->clear_counts();
TRY_READ(m_bit_stream->init_bool(m_header_size_in_bytes)); TRY_READ(m_bit_stream->init_bool(frame_context.header_size_in_bytes));
TRY(compressed_header()); TRY(compressed_header());
TRY_READ(m_bit_stream->exit_bool()); TRY_READ(m_bit_stream->exit_bool());
TRY(m_decoder.allocate_buffers()); TRY(m_decoder.allocate_buffers(frame_context));
TRY(decode_tiles()); TRY(decode_tiles(frame_context));
TRY(refresh_probs()); TRY(refresh_probs());
return {}; m_previous_frame_size = frame_context.size();
m_previous_show_frame = m_show_frame;
return frame_context;
} }
bool Parser::trailing_bits() bool Parser::trailing_bits()
@ -144,23 +148,25 @@ DecoderErrorOr<ColorRange> Parser::read_color_range()
} }
/* (6.2) */ /* (6.2) */
DecoderErrorOr<void> Parser::uncompressed_header() DecoderErrorOr<FrameContext> Parser::uncompressed_header()
{ {
FrameContext frame_context;
auto frame_marker = TRY_READ(m_bit_stream->read_bits(2)); auto frame_marker = TRY_READ(m_bit_stream->read_bits(2));
if (frame_marker != 2) if (frame_marker != 2)
return DecoderError::corrupted("uncompressed_header: Frame marker must be 2"sv); return DecoderError::corrupted("uncompressed_header: Frame marker must be 2"sv);
auto profile_low_bit = TRY_READ(m_bit_stream->read_bit()); auto profile_low_bit = TRY_READ(m_bit_stream->read_bit());
auto profile_high_bit = TRY_READ(m_bit_stream->read_bit()); auto profile_high_bit = TRY_READ(m_bit_stream->read_bit());
m_profile = (profile_high_bit << 1u) + profile_low_bit; frame_context.profile = (profile_high_bit << 1u) + profile_low_bit;
if (m_profile == 3 && TRY_READ(m_bit_stream->read_bit())) if (frame_context.profile == 3 && TRY_READ(m_bit_stream->read_bit()))
return DecoderError::corrupted("uncompressed_header: Profile 3 reserved bit was non-zero"sv); return DecoderError::corrupted("uncompressed_header: Profile 3 reserved bit was non-zero"sv);
m_show_existing_frame = TRY_READ(m_bit_stream->read_bit());
if (m_show_existing_frame) { if (TRY_READ(m_bit_stream->read_bit())) {
m_frame_to_show_map_index = TRY_READ(m_bit_stream->read_bits(3));
m_header_size_in_bytes = 0;
m_refresh_frame_flags = 0; m_refresh_frame_flags = 0;
m_loop_filter_level = 0; m_loop_filter_level = 0;
return {}; frame_context.set_existing_frame_to_show(TRY_READ(m_bit_stream->read_bits(3)));
return frame_context;
} }
m_last_frame_type = m_frame_type; m_last_frame_type = m_frame_type;
@ -168,11 +174,14 @@ DecoderErrorOr<void> Parser::uncompressed_header()
m_show_frame = TRY_READ(m_bit_stream->read_bit()); m_show_frame = TRY_READ(m_bit_stream->read_bit());
m_error_resilient_mode = TRY_READ(m_bit_stream->read_bit()); m_error_resilient_mode = TRY_READ(m_bit_stream->read_bit());
Gfx::Size<u32> frame_size;
Gfx::Size<u32> render_size;
if (m_frame_type == KeyFrame) { if (m_frame_type == KeyFrame) {
TRY(frame_sync_code()); TRY(frame_sync_code());
TRY(color_config()); TRY(color_config(frame_context));
m_frame_size = TRY(frame_size()); frame_size = TRY(parse_frame_size());
m_render_size = TRY(render_size(m_frame_size)); render_size = TRY(parse_render_size(frame_size));
m_refresh_frame_flags = 0xFF; m_refresh_frame_flags = 0xFF;
m_frame_is_intra = true; m_frame_is_intra = true;
} else { } else {
@ -186,8 +195,8 @@ DecoderErrorOr<void> Parser::uncompressed_header()
if (m_frame_is_intra) { if (m_frame_is_intra) {
TRY(frame_sync_code()); TRY(frame_sync_code());
if (m_profile > 0) { if (frame_context.profile > 0) {
TRY(color_config()); TRY(color_config(frame_context));
} else { } else {
m_color_space = Bt601; m_color_space = Bt601;
m_subsampling_x = true; m_subsampling_x = true;
@ -196,22 +205,24 @@ DecoderErrorOr<void> Parser::uncompressed_header()
} }
m_refresh_frame_flags = TRY_READ(m_bit_stream->read_f8()); m_refresh_frame_flags = TRY_READ(m_bit_stream->read_f8());
m_frame_size = TRY(frame_size()); frame_size = TRY(parse_frame_size());
m_render_size = TRY(render_size(m_frame_size)); render_size = TRY(parse_render_size(frame_size));
} else { } else {
m_refresh_frame_flags = TRY_READ(m_bit_stream->read_f8()); m_refresh_frame_flags = TRY_READ(m_bit_stream->read_f8());
for (auto i = 0; i < 3; i++) { for (auto i = 0; i < 3; i++) {
m_ref_frame_idx[i] = TRY_READ(m_bit_stream->read_bits(3)); m_ref_frame_idx[i] = TRY_READ(m_bit_stream->read_bits(3));
m_ref_frame_sign_bias[LastFrame + i] = TRY_READ(m_bit_stream->read_bit()); m_ref_frame_sign_bias[LastFrame + i] = TRY_READ(m_bit_stream->read_bit());
} }
m_frame_size = TRY(frame_size_with_refs()); frame_size = TRY(parse_frame_size_with_refs());
m_render_size = TRY(render_size(m_frame_size)); render_size = TRY(parse_render_size(frame_size));
m_allow_high_precision_mv = TRY_READ(m_bit_stream->read_bit()); m_allow_high_precision_mv = TRY_READ(m_bit_stream->read_bit());
TRY(read_interpolation_filter()); TRY(read_interpolation_filter());
} }
} }
compute_image_size(); DECODER_TRY_ALLOC(frame_context.set_size(frame_size));
frame_context.render_size = render_size;
TRY(compute_image_size(frame_context));
if (!m_error_resilient_mode) { if (!m_error_resilient_mode) {
m_refresh_frame_context = TRY_READ(m_bit_stream->read_bit()); m_refresh_frame_context = TRY_READ(m_bit_stream->read_bit());
@ -237,11 +248,11 @@ DecoderErrorOr<void> Parser::uncompressed_header()
TRY(loop_filter_params()); TRY(loop_filter_params());
TRY(quantization_params()); TRY(quantization_params());
TRY(segmentation_params()); TRY(segmentation_params());
TRY(tile_info()); TRY(tile_info(frame_context));
m_header_size_in_bytes = TRY_READ(m_bit_stream->read_f16()); frame_context.header_size_in_bytes = TRY_READ(m_bit_stream->read_f16());
return {}; return frame_context;
} }
DecoderErrorOr<void> Parser::frame_sync_code() DecoderErrorOr<void> Parser::frame_sync_code()
@ -255,9 +266,9 @@ DecoderErrorOr<void> Parser::frame_sync_code()
return {}; return {};
} }
DecoderErrorOr<void> Parser::color_config() DecoderErrorOr<void> Parser::color_config(FrameContext const& frame_context)
{ {
if (m_profile >= 2) { if (frame_context.profile >= 2) {
m_bit_depth = TRY_READ(m_bit_stream->read_bit()) ? 12 : 10; m_bit_depth = TRY_READ(m_bit_stream->read_bit()) ? 12 : 10;
} else { } else {
m_bit_depth = 8; m_bit_depth = 8;
@ -269,7 +280,7 @@ DecoderErrorOr<void> Parser::color_config()
if (color_space != RGB) { if (color_space != RGB) {
m_color_range = TRY(read_color_range()); m_color_range = TRY(read_color_range());
if (m_profile == 1 || m_profile == 3) { if (frame_context.profile == 1 || frame_context.profile == 3) {
m_subsampling_x = TRY_READ(m_bit_stream->read_bit()); m_subsampling_x = TRY_READ(m_bit_stream->read_bit());
m_subsampling_y = TRY_READ(m_bit_stream->read_bit()); m_subsampling_y = TRY_READ(m_bit_stream->read_bit());
if (TRY_READ(m_bit_stream->read_bit())) if (TRY_READ(m_bit_stream->read_bit()))
@ -280,7 +291,7 @@ DecoderErrorOr<void> Parser::color_config()
} }
} else { } else {
m_color_range = ColorRange::Full; m_color_range = ColorRange::Full;
if (m_profile == 1 || m_profile == 3) { if (frame_context.profile == 1 || frame_context.profile == 3) {
m_subsampling_x = false; m_subsampling_x = false;
m_subsampling_y = false; m_subsampling_y = false;
if (TRY_READ(m_bit_stream->read_bit())) if (TRY_READ(m_bit_stream->read_bit()))
@ -290,19 +301,19 @@ DecoderErrorOr<void> Parser::color_config()
return {}; return {};
} }
DecoderErrorOr<Gfx::Size<u32>> Parser::frame_size() DecoderErrorOr<Gfx::Size<u32>> Parser::parse_frame_size()
{ {
return Gfx::Size<u32> { TRY_READ(m_bit_stream->read_f16()) + 1, TRY_READ(m_bit_stream->read_f16()) + 1 }; return Gfx::Size<u32> { TRY_READ(m_bit_stream->read_f16()) + 1, TRY_READ(m_bit_stream->read_f16()) + 1 };
} }
DecoderErrorOr<Gfx::Size<u32>> Parser::render_size(Gfx::Size<u32> frame_size) DecoderErrorOr<Gfx::Size<u32>> Parser::parse_render_size(Gfx::Size<u32> frame_size)
{ {
if (!TRY_READ(m_bit_stream->read_bit())) if (!TRY_READ(m_bit_stream->read_bit()))
return frame_size; return frame_size;
return Gfx::Size<u32> { TRY_READ(m_bit_stream->read_f16()) + 1, TRY_READ(m_bit_stream->read_f16()) + 1 }; return Gfx::Size<u32> { TRY_READ(m_bit_stream->read_f16()) + 1, TRY_READ(m_bit_stream->read_f16()) + 1 };
} }
DecoderErrorOr<Gfx::Size<u32>> Parser::frame_size_with_refs() DecoderErrorOr<Gfx::Size<u32>> Parser::parse_frame_size_with_refs()
{ {
Optional<Gfx::Size<u32>> size; Optional<Gfx::Size<u32>> size;
for (auto frame_index : m_ref_frame_idx) { for (auto frame_index : m_ref_frame_idx) {
@ -315,29 +326,23 @@ DecoderErrorOr<Gfx::Size<u32>> Parser::frame_size_with_refs()
if (size.has_value()) if (size.has_value())
return size.value(); return size.value();
return TRY(frame_size()); return TRY(parse_frame_size());
} }
void Parser::compute_image_size() DecoderErrorOr<void> Parser::compute_image_size(FrameContext& frame_context)
{ {
auto new_cols = (m_frame_size.width() + 7u) >> 3u;
auto new_rows = (m_frame_size.height() + 7u) >> 3u;
// 7.2.6 Compute image size semantics // 7.2.6 Compute image size semantics
// When compute_image_size is invoked, the following ordered steps occur: // When compute_image_size is invoked, the following ordered steps occur:
// 1. If this is the first time compute_image_size is invoked, or if either FrameWidth or FrameHeight have // 1. If this is the first time compute_image_size is invoked, or if either FrameWidth or FrameHeight have
// changed in value compared to the previous time this function was invoked, then the segmentation map is // changed in value compared to the previous time this function was invoked, then the segmentation map is
// cleared to all zeros by setting SegmentId[ row ][ col ] equal to 0 for row = 0..MiRows-1 and col = // cleared to all zeros by setting SegmentId[ row ][ col ] equal to 0 for row = 0..MiRows-1 and col =
// 0..MiCols-1. // 0..MiCols-1.
bool first_invoke = !m_mi_cols && !m_mi_rows; // FIXME: What does this mean? SegmentIds is scoped to one frame, so it will not contain values here. It's
bool same_size = m_mi_cols == new_cols && m_mi_rows == new_rows; // also suspicious that spec refers to this as SegmentId rather than SegmentIds (plural). Is this
if (first_invoke || !same_size) { // supposed to refer to PrevSegmentIds?
// FIXME: Does this ever do anything? Segment IDs are already reset every frame. It's also suspicious bool first_invoke = m_is_first_compute_image_size_invoke;
// that spec refers to this as SegmentId rather than SegmentIds (plural). Is this supposed to m_is_first_compute_image_size_invoke = false;
// refer to PrevSegmentIds? bool same_size = m_previous_frame_size == frame_context.size();
for (size_t i = 0; i < m_frame_block_contexts.size(); i++)
m_frame_block_contexts[i].segment_id = 0;
}
// 2. The variable UsePrevFrameMvs is set equal to 1 if all of the following conditions are true: // 2. The variable UsePrevFrameMvs is set equal to 1 if all of the following conditions are true:
// a. This is not the first time compute_image_size is invoked. // a. This is not the first time compute_image_size is invoked.
@ -347,13 +352,8 @@ void Parser::compute_image_size()
// d. error_resilient_mode is equal to 0. // d. error_resilient_mode is equal to 0.
// e. FrameIsIntra is equal to 0. // e. FrameIsIntra is equal to 0.
// Otherwise, UsePrevFrameMvs is set equal to 0. // Otherwise, UsePrevFrameMvs is set equal to 0.
m_use_prev_frame_mvs = !first_invoke && same_size && m_prev_show_frame && !m_error_resilient_mode && !m_frame_is_intra; m_use_prev_frame_mvs = !first_invoke && same_size && m_previous_show_frame && !m_error_resilient_mode && !m_frame_is_intra;
m_prev_show_frame = m_show_frame; return {};
m_mi_cols = new_cols;
m_mi_rows = new_rows;
m_sb64_cols = (m_mi_cols + 7u) >> 3u;
m_sb64_rows = (m_mi_rows + 7u) >> 3u;
} }
DecoderErrorOr<void> Parser::read_interpolation_filter() DecoderErrorOr<void> Parser::read_interpolation_filter()
@ -452,10 +452,11 @@ DecoderErrorOr<u8> Parser::read_prob()
return 255; return 255;
} }
DecoderErrorOr<void> Parser::tile_info() DecoderErrorOr<void> Parser::tile_info(FrameContext& frame_context)
{ {
auto min_log2_tile_cols = calc_min_log2_tile_cols(); auto superblock_columns = frame_context.superblock_columns();
auto max_log2_tile_cols = calc_max_log2_tile_cols(); auto min_log2_tile_cols = calc_min_log2_tile_cols(superblock_columns);
auto max_log2_tile_cols = calc_max_log2_tile_cols(superblock_columns);
m_tile_cols_log2 = min_log2_tile_cols; m_tile_cols_log2 = min_log2_tile_cols;
while (m_tile_cols_log2 < max_log2_tile_cols) { while (m_tile_cols_log2 < max_log2_tile_cols) {
if (TRY_READ(m_bit_stream->read_bit())) if (TRY_READ(m_bit_stream->read_bit()))
@ -470,18 +471,18 @@ DecoderErrorOr<void> Parser::tile_info()
return {}; return {};
} }
u16 Parser::calc_min_log2_tile_cols() u16 Parser::calc_min_log2_tile_cols(u32 superblock_columns)
{ {
auto min_log_2 = 0u; auto min_log_2 = 0u;
while ((u32)(MAX_TILE_WIDTH_B64 << min_log_2) < m_sb64_cols) while ((u32)(MAX_TILE_WIDTH_B64 << min_log_2) < superblock_columns)
min_log_2++; min_log_2++;
return min_log_2; return min_log_2;
} }
u16 Parser::calc_max_log2_tile_cols() u16 Parser::calc_max_log2_tile_cols(u32 superblock_columns)
{ {
u16 max_log_2 = 1; u16 max_log_2 = 1;
while ((m_sb64_cols >> max_log_2) >= MIN_TILE_WIDTH_B64) while ((superblock_columns >> max_log_2) >= MIN_TILE_WIDTH_B64)
max_log_2++; max_log_2++;
return max_log_2 - 1; return max_log_2 - 1;
} }
@ -802,18 +803,12 @@ void Parser::setup_compound_reference_mode()
} }
} }
DecoderErrorOr<void> Parser::allocate_tile_data() DecoderErrorOr<void> Parser::decode_tiles(FrameContext& frame_context)
{
DECODER_TRY_ALLOC(m_frame_block_contexts.try_resize(m_mi_rows, m_mi_cols));
return {};
}
DecoderErrorOr<void> Parser::decode_tiles()
{ {
auto tile_cols = 1 << m_tile_cols_log2; auto tile_cols = 1 << m_tile_cols_log2;
auto tile_rows = 1 << m_tile_rows_log2; auto tile_rows = 1 << m_tile_rows_log2;
TRY(allocate_tile_data()); clear_above_context(frame_context);
clear_above_context();
for (auto tile_row = 0; tile_row < tile_rows; tile_row++) { for (auto tile_row = 0; tile_row < tile_rows; tile_row++) {
for (auto tile_col = 0; tile_col < tile_cols; tile_col++) { for (auto tile_col = 0; tile_col < tile_cols; tile_col++) {
auto last_tile = (tile_row == tile_rows - 1) && (tile_col == tile_cols - 1); auto last_tile = (tile_row == tile_rows - 1) && (tile_col == tile_cols - 1);
@ -823,12 +818,15 @@ DecoderErrorOr<void> Parser::decode_tiles()
else else
tile_size = TRY_READ(m_bit_stream->read_bits(32)); tile_size = TRY_READ(m_bit_stream->read_bits(32));
m_mi_row_start = get_tile_offset(tile_row, m_mi_rows, m_tile_rows_log2); auto rows_start = get_tile_offset(tile_row, frame_context.rows(), m_tile_rows_log2);
m_mi_row_end = get_tile_offset(tile_row + 1, m_mi_rows, m_tile_rows_log2); auto rows_end = get_tile_offset(tile_row + 1, frame_context.rows(), m_tile_rows_log2);
m_mi_col_start = get_tile_offset(tile_col, m_mi_cols, m_tile_cols_log2); auto columns_start = get_tile_offset(tile_col, frame_context.columns(), m_tile_cols_log2);
m_mi_col_end = get_tile_offset(tile_col + 1, m_mi_cols, m_tile_cols_log2); auto columns_end = get_tile_offset(tile_col + 1, frame_context.columns(), m_tile_cols_log2);
auto tile_context = TileContext(frame_context, rows_start, rows_end, columns_start, columns_end);
TRY_READ(m_bit_stream->init_bool(tile_size)); TRY_READ(m_bit_stream->init_bool(tile_size));
TRY(decode_tile()); TRY(decode_tile(tile_context));
TRY_READ(m_bit_stream->exit_bool()); TRY_READ(m_bit_stream->exit_bool());
} }
} }
@ -851,12 +849,12 @@ void Parser::clear_context(Vector<Vector<T>>& context, size_t outer_size, size_t
clear_context(sub_vector, inner_size); clear_context(sub_vector, inner_size);
} }
void Parser::clear_above_context() void Parser::clear_above_context(FrameContext& frame_context)
{ {
for (auto i = 0u; i < m_above_nonzero_context.size(); i++) for (auto i = 0u; i < m_above_nonzero_context.size(); i++)
clear_context(m_above_nonzero_context[i], 2 * m_mi_cols); clear_context(m_above_nonzero_context[i], 2 * frame_context.columns());
clear_context(m_above_seg_pred_context, m_mi_cols); clear_context(m_above_seg_pred_context, frame_context.columns());
clear_context(m_above_partition_context, m_sb64_cols * 8); clear_context(m_above_partition_context, frame_context.superblock_columns() * 8);
} }
u32 Parser::get_tile_offset(u32 tile_num, u32 mis, u32 tile_size_log2) u32 Parser::get_tile_offset(u32 tile_num, u32 mis, u32 tile_size_log2)
@ -866,51 +864,51 @@ u32 Parser::get_tile_offset(u32 tile_num, u32 mis, u32 tile_size_log2)
return min(offset, mis); return min(offset, mis);
} }
DecoderErrorOr<void> Parser::decode_tile() DecoderErrorOr<void> Parser::decode_tile(TileContext& tile_context)
{ {
for (auto row = m_mi_row_start; row < m_mi_row_end; row += 8) { for (auto row = tile_context.rows_start; row < tile_context.rows_end; row += 8) {
clear_left_context(); clear_left_context(tile_context);
for (auto col = m_mi_col_start; col < m_mi_col_end; col += 8) { for (auto col = tile_context.columns_start; col < tile_context.columns_end; col += 8) {
TRY(decode_partition(row, col, Block_64x64)); TRY(decode_partition(tile_context, row, col, Block_64x64));
} }
} }
return {}; return {};
} }
void Parser::clear_left_context() void Parser::clear_left_context(TileContext& tile_context)
{ {
for (auto i = 0u; i < m_left_nonzero_context.size(); i++) for (auto i = 0u; i < m_left_nonzero_context.size(); i++)
clear_context(m_left_nonzero_context[i], 2 * m_mi_rows); clear_context(m_left_nonzero_context[i], 2 * tile_context.frame_context.rows());
clear_context(m_left_seg_pred_context, m_mi_rows); clear_context(m_left_seg_pred_context, tile_context.frame_context.rows());
clear_context(m_left_partition_context, m_sb64_rows * 8); clear_context(m_left_partition_context, tile_context.frame_context.superblock_rows() * 8);
} }
DecoderErrorOr<void> Parser::decode_partition(u32 row, u32 column, BlockSubsize subsize) DecoderErrorOr<void> Parser::decode_partition(TileContext& tile_context, u32 row, u32 column, BlockSubsize subsize)
{ {
if (row >= m_mi_rows || column >= m_mi_cols) if (row >= tile_context.frame_context.rows() || column >= tile_context.frame_context.columns())
return {}; return {};
u8 num_8x8 = num_8x8_blocks_wide_lookup[subsize]; u8 num_8x8 = num_8x8_blocks_wide_lookup[subsize];
auto half_block_8x8 = num_8x8 >> 1; auto half_block_8x8 = num_8x8 >> 1;
bool has_rows = (row + half_block_8x8) < m_mi_rows; bool has_rows = (row + half_block_8x8) < tile_context.frame_context.rows();
bool has_cols = (column + half_block_8x8) < m_mi_cols; bool has_cols = (column + half_block_8x8) < tile_context.frame_context.columns();
auto partition = TRY_READ(TreeParser::parse_partition(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, has_rows, has_cols, subsize, num_8x8, m_above_partition_context, m_left_partition_context, row, column, m_frame_is_intra)); auto partition = TRY_READ(TreeParser::parse_partition(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, has_rows, has_cols, subsize, num_8x8, m_above_partition_context, m_left_partition_context, row, column, m_frame_is_intra));
auto child_subsize = subsize_lookup[partition][subsize]; auto child_subsize = subsize_lookup[partition][subsize];
if (child_subsize < Block_8x8 || partition == PartitionNone) { if (child_subsize < Block_8x8 || partition == PartitionNone) {
TRY(decode_block(row, column, child_subsize)); TRY(decode_block(tile_context, row, column, child_subsize));
} else if (partition == PartitionHorizontal) { } else if (partition == PartitionHorizontal) {
TRY(decode_block(row, column, child_subsize)); TRY(decode_block(tile_context, row, column, child_subsize));
if (has_rows) if (has_rows)
TRY(decode_block(row + half_block_8x8, column, child_subsize)); TRY(decode_block(tile_context, row + half_block_8x8, column, child_subsize));
} else if (partition == PartitionVertical) { } else if (partition == PartitionVertical) {
TRY(decode_block(row, column, child_subsize)); TRY(decode_block(tile_context, row, column, child_subsize));
if (has_cols) if (has_cols)
TRY(decode_block(row, column + half_block_8x8, child_subsize)); TRY(decode_block(tile_context, row, column + half_block_8x8, child_subsize));
} else { } else {
TRY(decode_partition(row, column, child_subsize)); TRY(decode_partition(tile_context, row, column, child_subsize));
TRY(decode_partition(row, column + half_block_8x8, child_subsize)); TRY(decode_partition(tile_context, row, column + half_block_8x8, child_subsize));
TRY(decode_partition(row + half_block_8x8, column, child_subsize)); TRY(decode_partition(tile_context, row + half_block_8x8, column, child_subsize));
TRY(decode_partition(row + half_block_8x8, column + half_block_8x8, child_subsize)); TRY(decode_partition(tile_context, row + half_block_8x8, column + half_block_8x8, child_subsize));
} }
if (subsize == Block_8x8 || partition != PartitionSplit) { if (subsize == Block_8x8 || partition != PartitionSplit) {
auto above_context = 15 >> b_width_log2_lookup[child_subsize]; auto above_context = 15 >> b_width_log2_lookup[child_subsize];
@ -923,65 +921,62 @@ DecoderErrorOr<void> Parser::decode_partition(u32 row, u32 column, BlockSubsize
return {}; return {};
} }
size_t Parser::get_image_index(u32 row, u32 column) const size_t Parser::get_image_index(FrameContext const& frame_context, u32 row, u32 column) const
{ {
VERIFY(row < m_mi_rows && column < m_mi_cols); VERIFY(row < frame_context.rows() && column < frame_context.columns());
return row * m_mi_cols + column; return row * frame_context.columns() + column;
} }
DecoderErrorOr<void> Parser::decode_block(u32 row, u32 column, BlockSubsize subsize) DecoderErrorOr<void> Parser::decode_block(TileContext& tile_context, u32 row, u32 column, BlockSubsize subsize)
{ {
m_mi_size = subsize; auto above_context = row > 0 ? tile_context.frame_block_contexts().at(row - 1, column) : FrameBlockContext();
auto above_context = row > 0 ? m_frame_block_contexts.at(row - 1, column) : FrameBlockContext(); auto left_context = column > tile_context.columns_start ? tile_context.frame_block_contexts().at(row, column - 1) : FrameBlockContext();
auto left_context = column > m_mi_col_start ? m_frame_block_contexts.at(row, column - 1) : FrameBlockContext(); auto block_context = BlockContext(tile_context, row, column, subsize);
TRY(mode_info(row, column, above_context, left_context));
auto had_residual_tokens = TRY(residual(row, column, above_context.is_available, left_context.is_available)); TRY(mode_info(block_context, above_context, left_context));
auto had_residual_tokens = TRY(residual(block_context, above_context.is_available, left_context.is_available));
if (m_is_inter && subsize >= Block_8x8 && !had_residual_tokens) if (m_is_inter && subsize >= Block_8x8 && !had_residual_tokens)
m_skip = true; m_skip = true;
// Spec doesn't specify whether it might index outside the frame here, but it seems that it can. Ensure that we don't for (size_t y = 0; y < block_context.contexts_view.height(); y++) {
// write out of bounds. This check seems consistent with libvpx. for (size_t x = 0; x < block_context.contexts_view.width(); x++) {
// See here: auto sub_block_context = FrameBlockContext { true, m_skip, m_tx_size, m_y_mode, m_block_sub_modes, m_interp_filter, m_ref_frame, m_block_mvs, m_segment_id };
// https://github.com/webmproject/libvpx/blob/705bf9de8c96cfe5301451f1d7e5c90a41c64e5f/vp9/decoder/vp9_decodeframe.c#L917 block_context.contexts_view.at(y, x) = sub_block_context;
auto maximum_block_y = min<u32>(num_8x8_blocks_high_lookup[subsize], m_mi_rows - row); VERIFY(block_context.frame_block_contexts().at(row + y, column + x).tx_size == sub_block_context.tx_size);
auto maximum_block_x = min<u32>(num_8x8_blocks_wide_lookup[subsize], m_mi_cols - column); }
for (size_t y = 0; y < maximum_block_y; y++) {
for (size_t x = 0; x < maximum_block_x; x++)
m_frame_block_contexts.at(row + y, column + x) = FrameBlockContext { true, m_skip, m_tx_size, m_y_mode, m_block_sub_modes, m_interp_filter, m_ref_frame, m_block_mvs, m_segment_id };
} }
return {}; return {};
} }
DecoderErrorOr<void> Parser::mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context) DecoderErrorOr<void> Parser::mode_info(BlockContext& block_context, FrameBlockContext above_context, FrameBlockContext left_context)
{ {
if (m_frame_is_intra) if (m_frame_is_intra)
TRY(intra_frame_mode_info(above_context, left_context)); TRY(intra_frame_mode_info(block_context, above_context, left_context));
else else
TRY(inter_frame_mode_info(row, column, above_context, left_context)); TRY(inter_frame_mode_info(block_context, above_context, left_context));
return {}; return {};
} }
DecoderErrorOr<void> Parser::intra_frame_mode_info(FrameBlockContext above_context, FrameBlockContext left_context) DecoderErrorOr<void> Parser::intra_frame_mode_info(BlockContext& block_context, FrameBlockContext above_context, FrameBlockContext left_context)
{ {
TRY(intra_segment_id()); TRY(intra_segment_id());
TRY(read_skip(above_context, left_context)); TRY(read_skip(above_context, left_context));
TRY(read_tx_size(above_context, left_context, true)); TRY(read_tx_size(block_context, above_context, left_context, true));
m_ref_frame[0] = IntraFrame; m_ref_frame[0] = IntraFrame;
m_ref_frame[1] = None; m_ref_frame[1] = None;
m_is_inter = false; m_is_inter = false;
// FIXME: This if statement is also present in parse_default_intra_mode. The selection of parameters for // FIXME: This if statement is also present in parse_default_intra_mode. The selection of parameters for
// the probability table lookup should be inlined here. // the probability table lookup should be inlined here.
if (m_mi_size >= Block_8x8) { if (block_context.size >= Block_8x8) {
m_y_mode = TRY_READ(TreeParser::parse_default_intra_mode(*m_bit_stream, *m_probability_tables, m_mi_size, above_context, left_context, m_block_sub_modes, 0, 0)); m_y_mode = TRY_READ(TreeParser::parse_default_intra_mode(*m_bit_stream, *m_probability_tables, block_context.size, above_context, left_context, m_block_sub_modes, 0, 0));
for (auto& block_sub_mode : m_block_sub_modes) for (auto& block_sub_mode : m_block_sub_modes)
block_sub_mode = m_y_mode; block_sub_mode = m_y_mode;
} else { } else {
m_num_4x4_w = num_4x4_blocks_wide_lookup[m_mi_size]; m_num_4x4_w = num_4x4_blocks_wide_lookup[block_context.size];
m_num_4x4_h = num_4x4_blocks_high_lookup[m_mi_size]; m_num_4x4_h = num_4x4_blocks_high_lookup[block_context.size];
for (auto idy = 0; idy < 2; idy += m_num_4x4_h) { for (auto idy = 0; idy < 2; idy += m_num_4x4_h) {
for (auto idx = 0; idx < 2; idx += m_num_4x4_w) { for (auto idx = 0; idx < 2; idx += m_num_4x4_w) {
auto sub_mode = TRY_READ(TreeParser::parse_default_intra_mode(*m_bit_stream, *m_probability_tables, m_mi_size, above_context, left_context, m_block_sub_modes, idx, idy)); auto sub_mode = TRY_READ(TreeParser::parse_default_intra_mode(*m_bit_stream, *m_probability_tables, block_context.size, above_context, left_context, m_block_sub_modes, idx, idy));
for (auto y = 0; y < m_num_4x4_h; y++) { for (auto y = 0; y < m_num_4x4_h; y++) {
for (auto x = 0; x < m_num_4x4_w; x++) { for (auto x = 0; x < m_num_4x4_w; x++) {
@ -1021,37 +1016,38 @@ bool Parser::seg_feature_active(u8 feature)
return m_segmentation_enabled && m_feature_enabled[m_segment_id][feature]; return m_segmentation_enabled && m_feature_enabled[m_segment_id][feature];
} }
DecoderErrorOr<void> Parser::read_tx_size(FrameBlockContext above_context, FrameBlockContext left_context, bool allow_select) DecoderErrorOr<void> Parser::read_tx_size(BlockContext const& block_context, FrameBlockContext above_context, FrameBlockContext left_context, bool allow_select)
{ {
m_max_tx_size = max_txsize_lookup[m_mi_size]; // FIXME: This probably doesn't need to set max_tx_size, and can also return the TXSize it reads.
if (allow_select && m_tx_mode == TXModeSelect && m_mi_size >= Block_8x8) m_max_tx_size = max_txsize_lookup[block_context.size];
if (allow_select && m_tx_mode == TXModeSelect && block_context.size >= Block_8x8)
m_tx_size = TRY_READ(TreeParser::parse_tx_size(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_max_tx_size, above_context, left_context)); m_tx_size = TRY_READ(TreeParser::parse_tx_size(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_max_tx_size, above_context, left_context));
else else
m_tx_size = min(m_max_tx_size, tx_mode_to_biggest_tx_size[m_tx_mode]); m_tx_size = min(m_max_tx_size, tx_mode_to_biggest_tx_size[m_tx_mode]);
return {}; return {};
} }
DecoderErrorOr<void> Parser::inter_frame_mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context) DecoderErrorOr<void> Parser::inter_frame_mode_info(BlockContext& block_context, FrameBlockContext above_context, FrameBlockContext left_context)
{ {
TRY(inter_segment_id(row, column)); TRY(inter_segment_id(block_context));
TRY(read_skip(above_context, left_context)); TRY(read_skip(above_context, left_context));
TRY(read_is_inter(above_context, left_context)); TRY(read_is_inter(above_context, left_context));
TRY(read_tx_size(above_context, left_context, !m_skip || !m_is_inter)); TRY(read_tx_size(block_context, above_context, left_context, !m_skip || !m_is_inter));
if (m_is_inter) { if (m_is_inter) {
TRY(inter_block_mode_info(row, column, above_context, left_context)); TRY(inter_block_mode_info(block_context, above_context, left_context));
} else { } else {
TRY(intra_block_mode_info()); TRY(intra_block_mode_info(block_context));
} }
return {}; return {};
} }
DecoderErrorOr<void> Parser::inter_segment_id(u32 row, u32 column) DecoderErrorOr<void> Parser::inter_segment_id(BlockContext const& block_context)
{ {
if (!m_segmentation_enabled) { if (!m_segmentation_enabled) {
m_segment_id = 0; m_segment_id = 0;
return {}; return {};
} }
auto predicted_segment_id = get_segment_id(row, column); auto predicted_segment_id = get_segment_id(block_context);
if (!m_segmentation_update_map) { if (!m_segmentation_update_map) {
m_segment_id = predicted_segment_id; m_segment_id = predicted_segment_id;
return {}; return {};
@ -1061,37 +1057,37 @@ DecoderErrorOr<void> Parser::inter_segment_id(u32 row, u32 column)
return {}; return {};
} }
auto seg_id_predicted = TRY_READ(TreeParser::parse_segment_id_predicted(*m_bit_stream, m_segmentation_pred_prob, m_left_seg_pred_context[row], m_above_seg_pred_context[column])); auto seg_id_predicted = TRY_READ(TreeParser::parse_segment_id_predicted(*m_bit_stream, m_segmentation_pred_prob, m_left_seg_pred_context[block_context.row], m_above_seg_pred_context[block_context.column]));
if (seg_id_predicted) if (seg_id_predicted)
m_segment_id = predicted_segment_id; m_segment_id = predicted_segment_id;
else else
m_segment_id = TRY_READ(TreeParser::parse_segment_id(*m_bit_stream, m_segmentation_tree_probs)); m_segment_id = TRY_READ(TreeParser::parse_segment_id(*m_bit_stream, m_segmentation_tree_probs));
for (size_t i = 0; i < num_8x8_blocks_wide_lookup[m_mi_size]; i++) { for (size_t i = 0; i < num_8x8_blocks_wide_lookup[block_context.size]; i++) {
auto index = column + i; auto index = block_context.column + i;
// (7.4.1) AboveSegPredContext[ i ] only needs to be set to 0 for i = 0..MiCols-1. // (7.4.1) AboveSegPredContext[ i ] only needs to be set to 0 for i = 0..MiCols-1.
if (index < m_above_seg_pred_context.size()) if (index < m_above_seg_pred_context.size())
m_above_seg_pred_context[index] = seg_id_predicted; m_above_seg_pred_context[index] = seg_id_predicted;
} }
for (size_t i = 0; i < num_8x8_blocks_high_lookup[m_mi_size]; i++) { for (size_t i = 0; i < num_8x8_blocks_high_lookup[block_context.size]; i++) {
auto index = row + i; auto index = block_context.row + i;
// (7.4.1) LeftSegPredContext[ i ] only needs to be set to 0 for i = 0..MiRows-1. // (7.4.1) LeftSegPredContext[ i ] only needs to be set to 0 for i = 0..MiRows-1.
if (index < m_above_seg_pred_context.size()) if (index < m_above_seg_pred_context.size())
m_left_seg_pred_context[row + i] = seg_id_predicted; m_left_seg_pred_context[block_context.row + i] = seg_id_predicted;
} }
return {}; return {};
} }
u8 Parser::get_segment_id(u32 row, u32 column) u8 Parser::get_segment_id(BlockContext const& block_context)
{ {
auto bw = num_8x8_blocks_wide_lookup[m_mi_size]; auto bw = num_8x8_blocks_wide_lookup[block_context.size];
auto bh = num_8x8_blocks_high_lookup[m_mi_size]; auto bh = num_8x8_blocks_high_lookup[block_context.size];
auto xmis = min(m_mi_cols - column, (u32)bw); auto xmis = min(block_context.frame_context.columns() - block_context.column, (u32)bw);
auto ymis = min(m_mi_rows - row, (u32)bh); auto ymis = min(block_context.frame_context.rows() - block_context.row, (u32)bh);
u8 segment = 7; u8 segment = 7;
for (size_t y = 0; y < ymis; y++) { for (size_t y = 0; y < ymis; y++) {
for (size_t x = 0; x < xmis; x++) { for (size_t x = 0; x < xmis; x++) {
segment = min(segment, m_previous_block_contexts.index_at(row + y, column + x)); segment = min(segment, m_previous_block_contexts.index_at(block_context.row + y, block_context.column + x));
} }
} }
return segment; return segment;
@ -1106,17 +1102,17 @@ DecoderErrorOr<void> Parser::read_is_inter(FrameBlockContext above_context, Fram
return {}; return {};
} }
DecoderErrorOr<void> Parser::intra_block_mode_info() DecoderErrorOr<void> Parser::intra_block_mode_info(BlockContext& block_context)
{ {
m_ref_frame[0] = IntraFrame; m_ref_frame[0] = IntraFrame;
m_ref_frame[1] = None; m_ref_frame[1] = None;
if (m_mi_size >= Block_8x8) { if (block_context.size >= Block_8x8) {
m_y_mode = TRY_READ(TreeParser::parse_intra_mode(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_mi_size)); m_y_mode = TRY_READ(TreeParser::parse_intra_mode(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, block_context.size));
for (auto& block_sub_mode : m_block_sub_modes) for (auto& block_sub_mode : m_block_sub_modes)
block_sub_mode = m_y_mode; block_sub_mode = m_y_mode;
} else { } else {
m_num_4x4_w = num_4x4_blocks_wide_lookup[m_mi_size]; m_num_4x4_w = num_4x4_blocks_wide_lookup[block_context.size];
m_num_4x4_h = num_4x4_blocks_high_lookup[m_mi_size]; m_num_4x4_h = num_4x4_blocks_high_lookup[block_context.size];
PredictionMode sub_intra_mode; PredictionMode sub_intra_mode;
for (auto idy = 0; idy < 2; idy += m_num_4x4_h) { for (auto idy = 0; idy < 2; idy += m_num_4x4_h) {
for (auto idx = 0; idx < 2; idx += m_num_4x4_w) { for (auto idx = 0; idx < 2; idx += m_num_4x4_w) {
@ -1133,34 +1129,34 @@ DecoderErrorOr<void> Parser::intra_block_mode_info()
return {}; return {};
} }
DecoderErrorOr<void> Parser::inter_block_mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context) DecoderErrorOr<void> Parser::inter_block_mode_info(BlockContext& block_context, FrameBlockContext above_context, FrameBlockContext left_context)
{ {
TRY(read_ref_frames(above_context, left_context)); TRY(read_ref_frames(above_context, left_context));
for (auto j = 0; j < 2; j++) { for (auto j = 0; j < 2; j++) {
if (m_ref_frame[j] > IntraFrame) { if (m_ref_frame[j] > IntraFrame) {
find_mv_refs(row, column, m_ref_frame[j], -1); find_mv_refs(block_context, m_ref_frame[j], -1);
find_best_ref_mvs(row, column, j); find_best_ref_mvs(block_context, j);
} }
} }
auto is_compound = m_ref_frame[1] > IntraFrame; auto is_compound = m_ref_frame[1] > IntraFrame;
if (seg_feature_active(SEG_LVL_SKIP)) { if (seg_feature_active(SEG_LVL_SKIP)) {
m_y_mode = PredictionMode::ZeroMv; m_y_mode = PredictionMode::ZeroMv;
} else if (m_mi_size >= Block_8x8) { } else if (block_context.size >= Block_8x8) {
m_y_mode = TRY_READ(TreeParser::parse_inter_mode(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_mode_context[m_ref_frame[0]])); m_y_mode = TRY_READ(TreeParser::parse_inter_mode(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_mode_context[m_ref_frame[0]]));
} }
if (m_interpolation_filter == Switchable) if (m_interpolation_filter == Switchable)
m_interp_filter = TRY_READ(TreeParser::parse_interpolation_filter(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, above_context, left_context)); m_interp_filter = TRY_READ(TreeParser::parse_interpolation_filter(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, above_context, left_context));
else else
m_interp_filter = m_interpolation_filter; m_interp_filter = m_interpolation_filter;
if (m_mi_size < Block_8x8) { if (block_context.size < Block_8x8) {
m_num_4x4_w = num_4x4_blocks_wide_lookup[m_mi_size]; m_num_4x4_w = num_4x4_blocks_wide_lookup[block_context.size];
m_num_4x4_h = num_4x4_blocks_high_lookup[m_mi_size]; m_num_4x4_h = num_4x4_blocks_high_lookup[block_context.size];
for (auto idy = 0; idy < 2; idy += m_num_4x4_h) { for (auto idy = 0; idy < 2; idy += m_num_4x4_h) {
for (auto idx = 0; idx < 2; idx += m_num_4x4_w) { for (auto idx = 0; idx < 2; idx += m_num_4x4_w) {
m_y_mode = TRY_READ(TreeParser::parse_inter_mode(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_mode_context[m_ref_frame[0]])); m_y_mode = TRY_READ(TreeParser::parse_inter_mode(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_mode_context[m_ref_frame[0]]));
if (m_y_mode == PredictionMode::NearestMv || m_y_mode == PredictionMode::NearMv) { if (m_y_mode == PredictionMode::NearestMv || m_y_mode == PredictionMode::NearMv) {
for (auto j = 0; j < 1 + is_compound; j++) for (auto j = 0; j < 1 + is_compound; j++)
append_sub8x8_mvs(row, column, idy * 2 + idx, j); append_sub8x8_mvs(block_context, idy * 2 + idx, j);
} }
TRY(assign_mv(is_compound)); TRY(assign_mv(is_compound));
for (auto y = 0; y < m_num_4x4_h; y++) { for (auto y = 0; y < m_num_4x4_h; y++) {
@ -1277,46 +1273,47 @@ DecoderErrorOr<i32> Parser::read_mv_component(u8 component)
return (mv_sign ? -1 : 1) * static_cast<i32>(magnitude); return (mv_sign ? -1 : 1) * static_cast<i32>(magnitude);
} }
Gfx::Point<size_t> Parser::get_decoded_point_for_plane(u32 column, u32 row, u8 plane) Gfx::Point<size_t> Parser::get_decoded_point_for_plane(FrameContext const& frame_context, u32 column, u32 row, u8 plane)
{ {
(void)frame_context;
if (plane == 0) if (plane == 0)
return { column * 8, row * 8 }; return { column * 8, row * 8 };
return { (column * 8) >> m_subsampling_x, (row * 8) >> m_subsampling_y }; return { (column * 8) >> m_subsampling_x, (row * 8) >> m_subsampling_y };
} }
Gfx::Size<size_t> Parser::get_decoded_size_for_plane(u8 plane) Gfx::Size<size_t> Parser::get_decoded_size_for_plane(FrameContext const& frame_context, u8 plane)
{ {
auto point = get_decoded_point_for_plane(m_mi_cols, m_mi_rows, plane); auto point = get_decoded_point_for_plane(frame_context, frame_context.columns(), frame_context.rows(), plane);
return { point.x(), point.y() }; return { point.x(), point.y() };
} }
DecoderErrorOr<bool> Parser::residual(u32 row, u32 column, bool has_block_above, bool has_block_left) DecoderErrorOr<bool> Parser::residual(BlockContext& block_context, bool has_block_above, bool has_block_left)
{ {
bool had_residual_tokens = false; bool had_residual_tokens = false;
auto block_size = m_mi_size < Block_8x8 ? Block_8x8 : static_cast<BlockSubsize>(m_mi_size); auto block_size = block_context.size < Block_8x8 ? Block_8x8 : block_context.size;
for (u8 plane = 0; plane < 3; plane++) { for (u8 plane = 0; plane < 3; plane++) {
auto tx_size = (plane > 0) ? get_uv_tx_size() : m_tx_size; auto tx_size = (plane > 0) ? get_uv_tx_size(block_context.size) : m_tx_size;
auto step = 1 << tx_size; auto step = 1 << tx_size;
auto plane_size = get_plane_block_size(block_size, plane); auto plane_size = get_plane_block_size(block_size, plane);
auto num_4x4_w = num_4x4_blocks_wide_lookup[plane_size]; auto num_4x4_w = num_4x4_blocks_wide_lookup[plane_size];
auto num_4x4_h = num_4x4_blocks_high_lookup[plane_size]; auto num_4x4_h = num_4x4_blocks_high_lookup[plane_size];
auto sub_x = (plane > 0) ? m_subsampling_x : 0; auto sub_x = (plane > 0) ? m_subsampling_x : 0;
auto sub_y = (plane > 0) ? m_subsampling_y : 0; auto sub_y = (plane > 0) ? m_subsampling_y : 0;
auto base_x = (column * 8) >> sub_x; auto base_x = (block_context.column * 8) >> sub_x;
auto base_y = (row * 8) >> sub_y; auto base_y = (block_context.row * 8) >> sub_y;
if (m_is_inter) { if (m_is_inter) {
if (m_mi_size < Block_8x8) { if (block_context.size < Block_8x8) {
for (auto y = 0; y < num_4x4_h; y++) { for (auto y = 0; y < num_4x4_h; y++) {
for (auto x = 0; x < num_4x4_w; x++) { for (auto x = 0; x < num_4x4_w; x++) {
TRY(m_decoder.predict_inter(plane, row, column, base_x + (4 * x), base_y + (4 * y), 4, 4, (y * num_4x4_w) + x)); TRY(m_decoder.predict_inter(plane, block_context, base_x + (4 * x), base_y + (4 * y), 4, 4, (y * num_4x4_w) + x));
} }
} }
} else { } else {
TRY(m_decoder.predict_inter(plane, row, column, base_x, base_y, num_4x4_w * 4, num_4x4_h * 4, 0)); TRY(m_decoder.predict_inter(plane, block_context, base_x, base_y, num_4x4_w * 4, num_4x4_h * 4, 0));
} }
} }
auto max_x = (m_mi_cols * 8) >> sub_x; auto max_x = (block_context.frame_context.columns() * 8) >> sub_x;
auto max_y = (m_mi_rows * 8) >> sub_y; auto max_y = (block_context.frame_context.rows() * 8) >> sub_y;
auto block_index = 0; auto block_index = 0;
for (auto y = 0; y < num_4x4_h; y += step) { for (auto y = 0; y < num_4x4_h; y += step) {
for (auto x = 0; x < num_4x4_w; x += step) { for (auto x = 0; x < num_4x4_w; x += step) {
@ -1325,11 +1322,11 @@ DecoderErrorOr<bool> Parser::residual(u32 row, u32 column, bool has_block_above,
auto non_zero = false; auto non_zero = false;
if (start_x < max_x && start_y < max_y) { if (start_x < max_x && start_y < max_y) {
if (!m_is_inter) if (!m_is_inter)
TRY(m_decoder.predict_intra(plane, start_x, start_y, has_block_left || x > 0, has_block_above || y > 0, (x + step) < num_4x4_w, tx_size, block_index)); TRY(m_decoder.predict_intra(plane, block_context, start_x, start_y, has_block_left || x > 0, has_block_above || y > 0, (x + step) < num_4x4_w, tx_size, block_index));
if (!m_skip) { if (!m_skip) {
non_zero = TRY(tokens(plane, start_x, start_y, tx_size, block_index)); non_zero = TRY(tokens(block_context, plane, start_x, start_y, tx_size, block_index));
had_residual_tokens = had_residual_tokens || non_zero; had_residual_tokens = had_residual_tokens || non_zero;
TRY(m_decoder.reconstruct(plane, start_x, start_y, tx_size)); TRY(m_decoder.reconstruct(plane, block_context, start_x, start_y, tx_size));
} }
} }
@ -1352,11 +1349,11 @@ DecoderErrorOr<bool> Parser::residual(u32 row, u32 column, bool has_block_above,
return had_residual_tokens; return had_residual_tokens;
} }
TXSize Parser::get_uv_tx_size() TXSize Parser::get_uv_tx_size(BlockSubsize size)
{ {
if (m_mi_size < Block_8x8) if (size < Block_8x8)
return TX_4x4; return TX_4x4;
return min(m_tx_size, max_txsize_lookup[get_plane_block_size(m_mi_size, 1)]); return min(m_tx_size, max_txsize_lookup[get_plane_block_size(size, 1)]);
} }
BlockSubsize Parser::get_plane_block_size(u32 subsize, u8 plane) BlockSubsize Parser::get_plane_block_size(u32 subsize, u8 plane)
@ -1366,16 +1363,16 @@ BlockSubsize Parser::get_plane_block_size(u32 subsize, u8 plane)
return ss_size_lookup[subsize][sub_x][sub_y]; return ss_size_lookup[subsize][sub_x][sub_y];
} }
DecoderErrorOr<bool> Parser::tokens(size_t plane, u32 start_x, u32 start_y, TXSize tx_size, u32 block_index) DecoderErrorOr<bool> Parser::tokens(BlockContext& block_context, size_t plane, u32 start_x, u32 start_y, TXSize tx_size, u32 block_index)
{ {
u32 segment_eob = 16 << (tx_size << 1); u32 segment_eob = 16 << (tx_size << 1);
auto const* scan = get_scan(plane, tx_size, block_index); auto const* scan = get_scan(block_context, plane, tx_size, block_index);
auto check_eob = true; auto check_eob = true;
u32 c = 0; u32 c = 0;
for (; c < segment_eob; c++) { for (; c < segment_eob; c++) {
auto pos = scan[c]; auto pos = scan[c];
auto band = (tx_size == TX_4x4) ? coefband_4x4[c] : coefband_8x8plus[c]; auto band = (tx_size == TX_4x4) ? coefband_4x4[c] : coefband_8x8plus[c];
auto tokens_context = TreeParser::get_tokens_context(m_subsampling_x, m_subsampling_y, m_mi_rows, m_mi_cols, m_above_nonzero_context, m_left_nonzero_context, m_token_cache, tx_size, m_tx_type, plane, start_x, start_y, pos, m_is_inter, band, c); auto tokens_context = TreeParser::get_tokens_context(m_subsampling_x, m_subsampling_y, block_context.frame_context.rows(), block_context.frame_context.columns(), m_above_nonzero_context, m_left_nonzero_context, m_token_cache, tx_size, m_tx_type, plane, start_x, start_y, pos, m_is_inter, band, c);
if (check_eob) { if (check_eob) {
auto more_coefs = TRY_READ(TreeParser::parse_more_coefficients(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, tokens_context)); auto more_coefs = TRY_READ(TreeParser::parse_more_coefficients(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, tokens_context));
if (!more_coefs) if (!more_coefs)
@ -1398,7 +1395,7 @@ DecoderErrorOr<bool> Parser::tokens(size_t plane, u32 start_x, u32 start_y, TXSi
return c > 0; return c > 0;
} }
u32 const* Parser::get_scan(size_t plane, TXSize tx_size, u32 block_index) u32 const* Parser::get_scan(BlockContext const& block_context, size_t plane, TXSize tx_size, u32 block_index)
{ {
if (plane > 0 || tx_size == TX_32x32) { if (plane > 0 || tx_size == TX_32x32) {
m_tx_type = DCT_DCT; m_tx_type = DCT_DCT;
@ -1406,7 +1403,7 @@ u32 const* Parser::get_scan(size_t plane, TXSize tx_size, u32 block_index)
if (m_lossless || m_is_inter) if (m_lossless || m_is_inter)
m_tx_type = DCT_DCT; m_tx_type = DCT_DCT;
else else
m_tx_type = mode_to_txfm_map[to_underlying(m_mi_size < Block_8x8 ? m_block_sub_modes[block_index] : m_y_mode)]; m_tx_type = mode_to_txfm_map[to_underlying(block_context.size < Block_8x8 ? m_block_sub_modes[block_index] : m_y_mode)];
} else { } else {
m_tx_type = mode_to_txfm_map[to_underlying(m_y_mode)]; m_tx_type = mode_to_txfm_map[to_underlying(m_y_mode)];
} }
@ -1452,15 +1449,15 @@ DecoderErrorOr<i32> Parser::read_coef(Token token)
return coef; return coef;
} }
bool Parser::is_inside(i32 row, i32 column) static bool is_inside(TileContext const& tile_context, MotionVector vector)
{ {
if (row < 0) if (vector.row() < 0)
return false; return false;
if (column < 0) if (vector.column() < 0)
return false; return false;
u32 row_positive = row; u32 row_positive = vector.row();
u32 column_positive = column; u32 column_positive = vector.column();
return row_positive < m_mi_rows && column_positive >= m_mi_col_start && column_positive < m_mi_col_end; return row_positive < tile_context.frame_context.rows() && column_positive >= tile_context.columns_start && column_positive < tile_context.columns_end;
} }
void Parser::add_mv_ref_list(u8 ref_list) void Parser::add_mv_ref_list(u8 ref_list)
@ -1474,23 +1471,23 @@ void Parser::add_mv_ref_list(u8 ref_list)
m_ref_mv_count++; m_ref_mv_count++;
} }
void Parser::get_block_mv(u32 candidate_row, u32 candidate_column, u8 ref_list, bool use_prev) void Parser::get_block_mv(BlockContext const& block_context, MotionVector candidate_vector, u8 ref_list, bool use_prev)
{ {
if (use_prev) { if (use_prev) {
auto const& prev_context = m_previous_block_contexts.at(candidate_row, candidate_column); auto const& prev_context = m_previous_block_contexts.at(candidate_vector.row(), candidate_vector.column());
m_candidate_mv[ref_list] = prev_context.primary_motion_vector_pair[ref_list]; m_candidate_mv[ref_list] = prev_context.primary_motion_vector_pair[ref_list];
m_candidate_frame[ref_list] = prev_context.ref_frames[ref_list]; m_candidate_frame[ref_list] = prev_context.ref_frames[ref_list];
} else { } else {
auto const& current_context = m_frame_block_contexts.at(candidate_row, candidate_column); auto const& current_context = block_context.frame_block_contexts().at(candidate_vector.row(), candidate_vector.column());
m_candidate_mv[ref_list] = current_context.primary_motion_vector_pair()[ref_list]; m_candidate_mv[ref_list] = current_context.primary_motion_vector_pair()[ref_list];
m_candidate_frame[ref_list] = current_context.ref_frames[ref_list]; m_candidate_frame[ref_list] = current_context.ref_frames[ref_list];
} }
} }
void Parser::if_same_ref_frame_add_mv(u32 candidate_row, u32 candidate_column, ReferenceFrameType ref_frame, bool use_prev) void Parser::if_same_ref_frame_add_mv(BlockContext const& block_context, MotionVector candidate_vector, ReferenceFrameType ref_frame, bool use_prev)
{ {
for (auto ref_list = 0u; ref_list < 2; ref_list++) { for (auto ref_list = 0u; ref_list < 2; ref_list++) {
get_block_mv(candidate_row, candidate_column, ref_list, use_prev); get_block_mv(block_context, candidate_vector, ref_list, use_prev);
if (m_candidate_frame[ref_list] == ref_frame) { if (m_candidate_frame[ref_list] == ref_frame) {
add_mv_ref_list(ref_list); add_mv_ref_list(ref_list);
return; return;
@ -1505,10 +1502,10 @@ void Parser::scale_mv(u8 ref_list, ReferenceFrameType ref_frame)
m_candidate_mv[ref_list] *= -1; m_candidate_mv[ref_list] *= -1;
} }
void Parser::if_diff_ref_frame_add_mv(u32 candidate_row, u32 candidate_column, ReferenceFrameType ref_frame, bool use_prev) void Parser::if_diff_ref_frame_add_mv(BlockContext const& block_context, MotionVector candidate_vector, ReferenceFrameType ref_frame, bool use_prev)
{ {
for (auto ref_list = 0u; ref_list < 2; ref_list++) for (auto ref_list = 0u; ref_list < 2; ref_list++)
get_block_mv(candidate_row, candidate_column, ref_list, use_prev); get_block_mv(block_context, candidate_vector, ref_list, use_prev);
auto mvs_are_same = m_candidate_mv[0] == m_candidate_mv[1]; auto mvs_are_same = m_candidate_mv[0] == m_candidate_mv[1];
if (m_candidate_frame[0] > ReferenceFrameType::IntraFrame && m_candidate_frame[0] != ref_frame) { if (m_candidate_frame[0] > ReferenceFrameType::IntraFrame && m_candidate_frame[0] != ref_frame) {
scale_mv(0, ref_frame); scale_mv(0, ref_frame);
@ -1520,16 +1517,16 @@ void Parser::if_diff_ref_frame_add_mv(u32 candidate_row, u32 candidate_column, R
} }
} }
MotionVector Parser::clamp_mv(u32 row, u32 column, MotionVector vector, i32 border) MotionVector Parser::clamp_mv(BlockContext const& block_context, MotionVector vector, i32 border)
{ {
i32 blocks_high = num_8x8_blocks_high_lookup[m_mi_size]; i32 blocks_high = num_8x8_blocks_high_lookup[block_context.size];
// Casts must be done here to prevent subtraction underflow from wrapping the values. // Casts must be done here to prevent subtraction underflow from wrapping the values.
i32 mb_to_top_edge = -8 * (static_cast<i32>(row) * MI_SIZE); i32 mb_to_top_edge = -8 * (static_cast<i32>(block_context.row) * MI_SIZE);
i32 mb_to_bottom_edge = 8 * ((static_cast<i32>(m_mi_rows) - blocks_high - static_cast<i32>(row)) * MI_SIZE); i32 mb_to_bottom_edge = 8 * ((static_cast<i32>(block_context.frame_context.rows()) - blocks_high - static_cast<i32>(block_context.row)) * MI_SIZE);
i32 blocks_wide = num_8x8_blocks_wide_lookup[m_mi_size]; i32 blocks_wide = num_8x8_blocks_wide_lookup[block_context.size];
i32 mb_to_left_edge = -8 * (static_cast<i32>(column) * MI_SIZE); i32 mb_to_left_edge = -8 * (static_cast<i32>(block_context.column) * MI_SIZE);
i32 mb_to_right_edge = 8 * ((static_cast<i32>(m_mi_cols) - blocks_wide - static_cast<i32>(column)) * MI_SIZE); i32 mb_to_right_edge = 8 * ((static_cast<i32>(block_context.frame_context.columns()) - blocks_wide - static_cast<i32>(block_context.column)) * MI_SIZE);
return { return {
clip_3(mb_to_top_edge - border, mb_to_bottom_edge + border, vector.row()), clip_3(mb_to_top_edge - border, mb_to_bottom_edge + border, vector.row()),
@ -1537,14 +1534,15 @@ MotionVector Parser::clamp_mv(u32 row, u32 column, MotionVector vector, i32 bord
}; };
} }
void Parser::clamp_mv_ref(u32 row, u32 column, u8 i) void Parser::clamp_mv_ref(BlockContext const& block_context, u8 i)
{ {
// FIXME: This seems silly and should probably just be written inline in the one place it's used.
MotionVector& vector = m_ref_list_mv[i]; MotionVector& vector = m_ref_list_mv[i];
vector = clamp_mv(row, column, vector, MV_BORDER); vector = clamp_mv(block_context, vector, MV_BORDER);
} }
// 6.5.1 Find MV refs syntax // 6.5.1 Find MV refs syntax
void Parser::find_mv_refs(u32 row, u32 column, ReferenceFrameType reference_frame, i32 block) void Parser::find_mv_refs(BlockContext& block_context, ReferenceFrameType reference_frame, i32 block)
{ {
m_ref_mv_count = 0; m_ref_mv_count = 0;
bool different_ref_found = false; bool different_ref_found = false;
@ -1553,15 +1551,15 @@ void Parser::find_mv_refs(u32 row, u32 column, ReferenceFrameType reference_fram
m_ref_list_mv[0] = {}; m_ref_list_mv[0] = {};
m_ref_list_mv[1] = {}; m_ref_list_mv[1] = {};
MotionVector base_coordinates = MotionVector(row, column); MotionVector base_coordinates = MotionVector(block_context.row, block_context.column);
for (auto i = 0u; i < 2; i++) { for (auto i = 0u; i < 2; i++) {
auto offset_vector = mv_ref_blocks[m_mi_size][i]; auto offset_vector = mv_ref_blocks[block_context.size][i];
auto candidate = base_coordinates + offset_vector; auto candidate = base_coordinates + offset_vector;
if (is_inside(candidate.row(), candidate.column())) { if (is_inside(block_context.tile_context, candidate)) {
different_ref_found = true; different_ref_found = true;
auto context = m_frame_block_contexts.at(candidate.row(), candidate.column()); auto context = block_context.frame_block_contexts().at(candidate.row(), candidate.column());
context_counter += mode_2_counter[to_underlying(context.y_mode)]; context_counter += mode_2_counter[to_underlying(context.y_mode)];
for (auto ref_list = 0u; ref_list < 2; ref_list++) { for (auto ref_list = 0u; ref_list < 2; ref_list++) {
@ -1584,28 +1582,28 @@ void Parser::find_mv_refs(u32 row, u32 column, ReferenceFrameType reference_fram
} }
for (auto i = 2u; i < MVREF_NEIGHBOURS; i++) { for (auto i = 2u; i < MVREF_NEIGHBOURS; i++) {
MotionVector candidate = base_coordinates + mv_ref_blocks[m_mi_size][i]; MotionVector candidate = base_coordinates + mv_ref_blocks[block_context.size][i];
if (is_inside(candidate.row(), candidate.column())) { if (is_inside(block_context.tile_context, candidate)) {
different_ref_found = true; different_ref_found = true;
if_same_ref_frame_add_mv(candidate.row(), candidate.column(), reference_frame, false); if_same_ref_frame_add_mv(block_context, candidate, reference_frame, false);
} }
} }
if (m_use_prev_frame_mvs) if (m_use_prev_frame_mvs)
if_same_ref_frame_add_mv(row, column, reference_frame, true); if_same_ref_frame_add_mv(block_context, base_coordinates, reference_frame, true);
if (different_ref_found) { if (different_ref_found) {
for (auto i = 0u; i < MVREF_NEIGHBOURS; i++) { for (auto i = 0u; i < MVREF_NEIGHBOURS; i++) {
MotionVector candidate = base_coordinates + mv_ref_blocks[m_mi_size][i]; MotionVector candidate = base_coordinates + mv_ref_blocks[block_context.size][i];
if (is_inside(candidate.row(), candidate.column())) if (is_inside(block_context.tile_context, candidate))
if_diff_ref_frame_add_mv(candidate.row(), candidate.column(), reference_frame, false); if_diff_ref_frame_add_mv(block_context, candidate, reference_frame, false);
} }
} }
if (m_use_prev_frame_mvs) if (m_use_prev_frame_mvs)
if_diff_ref_frame_add_mv(row, column, reference_frame, true); if_diff_ref_frame_add_mv(block_context, base_coordinates, reference_frame, true);
m_mode_context[reference_frame] = counter_to_context[context_counter]; m_mode_context[reference_frame] = counter_to_context[context_counter];
for (auto i = 0u; i < MAX_MV_REF_CANDIDATES; i++) for (auto i = 0u; i < MAX_MV_REF_CANDIDATES; i++)
clamp_mv_ref(row, column, i); clamp_mv_ref(block_context, i);
} }
bool Parser::use_mv_hp(MotionVector const& vector) bool Parser::use_mv_hp(MotionVector const& vector)
@ -1613,7 +1611,7 @@ bool Parser::use_mv_hp(MotionVector const& vector)
return (abs(vector.row()) >> 3) < COMPANDED_MVREF_THRESH && (abs(vector.column()) >> 3) < COMPANDED_MVREF_THRESH; return (abs(vector.row()) >> 3) < COMPANDED_MVREF_THRESH && (abs(vector.column()) >> 3) < COMPANDED_MVREF_THRESH;
} }
void Parser::find_best_ref_mvs(u32 row, u32 column, u8 ref_list) void Parser::find_best_ref_mvs(BlockContext& block_context, u8 ref_list)
{ {
for (auto i = 0u; i < MAX_MV_REF_CANDIDATES; i++) { for (auto i = 0u; i < MAX_MV_REF_CANDIDATES; i++) {
auto delta = m_ref_list_mv[i]; auto delta = m_ref_list_mv[i];
@ -1626,7 +1624,7 @@ void Parser::find_best_ref_mvs(u32 row, u32 column, u8 ref_list)
delta_column += delta_column > 0 ? -1 : 1; delta_column += delta_column > 0 ? -1 : 1;
} }
delta = { delta_row, delta_column }; delta = { delta_row, delta_column };
m_ref_list_mv[i] = clamp_mv(row, column, delta, (BORDERINPIXELS - INTERP_EXTEND) << 3); m_ref_list_mv[i] = clamp_mv(block_context, delta, (BORDERINPIXELS - INTERP_EXTEND) << 3);
} }
m_nearest_mv[ref_list] = m_ref_list_mv[0]; m_nearest_mv[ref_list] = m_ref_list_mv[0];
@ -1634,10 +1632,10 @@ void Parser::find_best_ref_mvs(u32 row, u32 column, u8 ref_list)
m_best_mv[ref_list] = m_ref_list_mv[0]; m_best_mv[ref_list] = m_ref_list_mv[0];
} }
void Parser::append_sub8x8_mvs(u32 row, u32 column, i32 block, u8 ref_list) void Parser::append_sub8x8_mvs(BlockContext& block_context, i32 block, u8 ref_list)
{ {
MotionVector sub_8x8_mvs[2]; MotionVector sub_8x8_mvs[2];
find_mv_refs(row, column, m_ref_frame[ref_list], block); find_mv_refs(block_context, m_ref_frame[ref_list], block);
auto destination_index = 0; auto destination_index = 0;
if (block == 0) { if (block == 0) {
for (auto i = 0u; i < 2; i++) for (auto i = 0u; i < 2; i++)

View file

@ -34,7 +34,7 @@ class Parser {
public: public:
explicit Parser(Decoder&); explicit Parser(Decoder&);
~Parser(); ~Parser();
DecoderErrorOr<void> parse_frame(ReadonlyBytes); DecoderErrorOr<FrameContext> parse_frame(ReadonlyBytes);
private: private:
/* Annex B: Superframes are a method of storing multiple coded frames into a single chunk /* Annex B: Superframes are a method of storing multiple coded frames into a single chunk
@ -49,30 +49,29 @@ private:
void clear_context(Vector<T>& context, size_t size); void clear_context(Vector<T>& context, size_t size);
template<typename T> template<typename T>
void clear_context(Vector<Vector<T>>& context, size_t outer_size, size_t inner_size); void clear_context(Vector<Vector<T>>& context, size_t outer_size, size_t inner_size);
DecoderErrorOr<void> allocate_tile_data();
/* (6.1) Frame Syntax */ /* (6.1) Frame Syntax */
bool trailing_bits(); bool trailing_bits();
DecoderErrorOr<void> refresh_probs(); DecoderErrorOr<void> refresh_probs();
/* (6.2) Uncompressed Header Syntax */ /* (6.2) Uncompressed Header Syntax */
DecoderErrorOr<void> uncompressed_header(); DecoderErrorOr<FrameContext> uncompressed_header();
DecoderErrorOr<void> frame_sync_code(); DecoderErrorOr<void> frame_sync_code();
DecoderErrorOr<void> color_config(); DecoderErrorOr<void> color_config(FrameContext const&);
DecoderErrorOr<void> set_frame_size_and_compute_image_size(); DecoderErrorOr<void> set_frame_size_and_compute_image_size();
DecoderErrorOr<Gfx::Size<u32>> frame_size(); DecoderErrorOr<Gfx::Size<u32>> parse_frame_size();
DecoderErrorOr<Gfx::Size<u32>> frame_size_with_refs(); DecoderErrorOr<Gfx::Size<u32>> parse_frame_size_with_refs();
DecoderErrorOr<Gfx::Size<u32>> render_size(Gfx::Size<u32> frame_size); DecoderErrorOr<Gfx::Size<u32>> parse_render_size(Gfx::Size<u32> frame_size);
void compute_image_size(); DecoderErrorOr<void> compute_image_size(FrameContext&);
DecoderErrorOr<void> read_interpolation_filter(); DecoderErrorOr<void> read_interpolation_filter();
DecoderErrorOr<void> loop_filter_params(); DecoderErrorOr<void> loop_filter_params();
DecoderErrorOr<void> quantization_params(); DecoderErrorOr<void> quantization_params();
DecoderErrorOr<i8> read_delta_q(); DecoderErrorOr<i8> read_delta_q();
DecoderErrorOr<void> segmentation_params(); DecoderErrorOr<void> segmentation_params();
DecoderErrorOr<u8> read_prob(); DecoderErrorOr<u8> read_prob();
DecoderErrorOr<void> tile_info(); DecoderErrorOr<void> tile_info(FrameContext&);
u16 calc_min_log2_tile_cols(); u16 calc_min_log2_tile_cols(u32 superblock_columns);
u16 calc_max_log2_tile_cols(); u16 calc_max_log2_tile_cols(u32 superblock_columns);
void setup_past_independence(); void setup_past_independence();
/* (6.3) Compressed Header Syntax */ /* (6.3) Compressed Header Syntax */
@ -97,58 +96,53 @@ private:
void setup_compound_reference_mode(); void setup_compound_reference_mode();
/* (6.4) Decode Tiles Syntax */ /* (6.4) Decode Tiles Syntax */
DecoderErrorOr<void> decode_tiles(); DecoderErrorOr<void> decode_tiles(FrameContext&);
void clear_above_context(); void clear_above_context(FrameContext&);
u32 get_tile_offset(u32 tile_num, u32 mis, u32 tile_size_log2); u32 get_tile_offset(u32 tile_num, u32 mis, u32 tile_size_log2);
DecoderErrorOr<void> decode_tile(); DecoderErrorOr<void> decode_tile(TileContext&);
void clear_left_context(); void clear_left_context(TileContext&);
DecoderErrorOr<void> decode_partition(u32 row, u32 column, BlockSubsize subsize); DecoderErrorOr<void> decode_partition(TileContext&, u32 row, u32 column, BlockSubsize subsize);
DecoderErrorOr<void> decode_block(u32 row, u32 column, BlockSubsize subsize); DecoderErrorOr<void> decode_block(TileContext&, u32 row, u32 column, BlockSubsize subsize);
DecoderErrorOr<void> mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context); DecoderErrorOr<void> mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
DecoderErrorOr<void> intra_frame_mode_info(FrameBlockContext above_context, FrameBlockContext left_context); DecoderErrorOr<void> intra_frame_mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
DecoderErrorOr<void> intra_segment_id(); DecoderErrorOr<void> intra_segment_id();
DecoderErrorOr<void> read_skip(FrameBlockContext above_context, FrameBlockContext left_context); DecoderErrorOr<void> read_skip(FrameBlockContext above_context, FrameBlockContext left_context);
bool seg_feature_active(u8 feature); bool seg_feature_active(u8 feature);
DecoderErrorOr<void> read_tx_size(FrameBlockContext above_context, FrameBlockContext left_context, bool allow_select); DecoderErrorOr<void> read_tx_size(BlockContext const&, FrameBlockContext above_context, FrameBlockContext left_context, bool allow_select);
DecoderErrorOr<void> inter_frame_mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context); DecoderErrorOr<void> inter_frame_mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
DecoderErrorOr<void> inter_segment_id(u32 row, u32 column); DecoderErrorOr<void> inter_segment_id(BlockContext const&);
u8 get_segment_id(u32 row, u32 column); u8 get_segment_id(BlockContext const&);
DecoderErrorOr<void> read_is_inter(FrameBlockContext above_context, FrameBlockContext left_context); DecoderErrorOr<void> read_is_inter(FrameBlockContext above_context, FrameBlockContext left_context);
DecoderErrorOr<void> intra_block_mode_info(); DecoderErrorOr<void> intra_block_mode_info(BlockContext&);
DecoderErrorOr<void> inter_block_mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context); DecoderErrorOr<void> inter_block_mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
DecoderErrorOr<void> read_ref_frames(FrameBlockContext above_context, FrameBlockContext left_context); DecoderErrorOr<void> read_ref_frames(FrameBlockContext above_context, FrameBlockContext left_context);
DecoderErrorOr<void> assign_mv(bool is_compound); DecoderErrorOr<void> assign_mv(bool is_compound);
DecoderErrorOr<void> read_mv(u8 ref); DecoderErrorOr<void> read_mv(u8 ref);
DecoderErrorOr<i32> read_mv_component(u8 component); DecoderErrorOr<i32> read_mv_component(u8 component);
DecoderErrorOr<bool> residual(u32 row, u32 column, bool has_block_above, bool has_block_left); DecoderErrorOr<bool> residual(BlockContext&, bool has_block_above, bool has_block_left);
TXSize get_uv_tx_size(); TXSize get_uv_tx_size(BlockSubsize size);
BlockSubsize get_plane_block_size(u32 subsize, u8 plane); BlockSubsize get_plane_block_size(u32 subsize, u8 plane);
DecoderErrorOr<bool> tokens(size_t plane, u32 x, u32 y, TXSize tx_size, u32 block_index); DecoderErrorOr<bool> tokens(BlockContext&, size_t plane, u32 x, u32 y, TXSize tx_size, u32 block_index);
u32 const* get_scan(size_t plane, TXSize tx_size, u32 block_index); u32 const* get_scan(BlockContext const&, size_t plane, TXSize tx_size, u32 block_index);
DecoderErrorOr<i32> read_coef(Token token); DecoderErrorOr<i32> read_coef(Token token);
/* (6.5) Motion Vector Prediction */ /* (6.5) Motion Vector Prediction */
void find_mv_refs(u32 row, u32 column, ReferenceFrameType, i32 block); void find_mv_refs(BlockContext&, ReferenceFrameType, i32 block);
void find_best_ref_mvs(u32 row, u32 column, u8 ref_list); void find_best_ref_mvs(BlockContext&, u8 ref_list);
bool use_mv_hp(MotionVector const& delta_mv); bool use_mv_hp(MotionVector const& delta_mv);
void append_sub8x8_mvs(u32 row, u32 column, i32 block, u8 ref_list); void append_sub8x8_mvs(BlockContext&, i32 block, u8 ref_list);
bool is_inside(i32 row, i32 column); void clamp_mv_ref(BlockContext const&, u8 i);
void clamp_mv_ref(u32 row, u32 column, u8 i); MotionVector clamp_mv(BlockContext const&, MotionVector vector, i32 border);
MotionVector clamp_mv(u32 row, u32 column, MotionVector mvec, i32 border); size_t get_image_index(FrameContext const&, u32 row, u32 column) const;
size_t get_image_index(u32 row, u32 column) const; void get_block_mv(BlockContext const&, MotionVector candidate_vector, u8 ref_list, bool use_prev);
void get_block_mv(u32 candidate_row, u32 candidate_column, u8 ref_list, bool use_prev); void if_same_ref_frame_add_mv(BlockContext const&, MotionVector candidate_vector, ReferenceFrameType ref_frame, bool use_prev);
void if_same_ref_frame_add_mv(u32 candidate_row, u32 candidate_column, ReferenceFrameType ref_frame, bool use_prev); void if_diff_ref_frame_add_mv(BlockContext const&, MotionVector candidate_vector, ReferenceFrameType ref_frame, bool use_prev);
void if_diff_ref_frame_add_mv(u32 candidate_row, u32 candidate_column, ReferenceFrameType ref_frame, bool use_prev);
void scale_mv(u8 ref_list, ReferenceFrameType ref_frame); void scale_mv(u8 ref_list, ReferenceFrameType ref_frame);
void add_mv_ref_list(u8 ref_list); void add_mv_ref_list(u8 ref_list);
Gfx::Point<size_t> get_decoded_point_for_plane(u32 row, u32 column, u8 plane); Gfx::Point<size_t> get_decoded_point_for_plane(FrameContext const&, u32 row, u32 column, u8 plane);
Gfx::Size<size_t> get_decoded_size_for_plane(u8 plane); Gfx::Size<size_t> get_decoded_size_for_plane(FrameContext const&, u8 plane);
u8 m_profile { 0 };
bool m_show_existing_frame { false };
u8 m_frame_to_show_map_index { 0 };
u16 m_header_size_in_bytes { 0 };
u8 m_refresh_frame_flags { 0 }; u8 m_refresh_frame_flags { 0 };
u8 m_loop_filter_level { 0 }; u8 m_loop_filter_level { 0 };
u8 m_loop_filter_sharpness { 0 }; u8 m_loop_filter_sharpness { 0 };
@ -156,7 +150,6 @@ private:
FrameType m_frame_type { FrameType::KeyFrame }; FrameType m_frame_type { FrameType::KeyFrame };
FrameType m_last_frame_type { FrameType::KeyFrame }; FrameType m_last_frame_type { FrameType::KeyFrame };
bool m_show_frame { false }; bool m_show_frame { false };
bool m_prev_show_frame { false };
bool m_error_resilient_mode { false }; bool m_error_resilient_mode { false };
bool m_frame_is_intra { false }; bool m_frame_is_intra { false };
u8 m_reset_frame_context { 0 }; u8 m_reset_frame_context { 0 };
@ -171,13 +164,9 @@ private:
ColorRange m_color_range; ColorRange m_color_range;
bool m_subsampling_x { false }; bool m_subsampling_x { false };
bool m_subsampling_y { false }; bool m_subsampling_y { false };
Gfx::Size<u32> m_frame_size { 0, 0 }; bool m_is_first_compute_image_size_invoke { true };
Gfx::Size<u32> m_render_size { 0, 0 }; Gfx::Size<u32> m_previous_frame_size { 0, 0 };
bool m_render_and_frame_size_different { false }; bool m_previous_show_frame { false };
u32 m_mi_cols { 0 };
u32 m_mi_rows { 0 };
u32 m_sb64_cols { 0 };
u32 m_sb64_rows { 0 };
InterpolationFilter m_interpolation_filter { 0xf }; InterpolationFilter m_interpolation_filter { 0xf };
u8 m_base_q_idx { 0 }; u8 m_base_q_idx { 0 };
i8 m_delta_q_y_dc { 0 }; i8 m_delta_q_y_dc { 0 };
@ -205,12 +194,6 @@ private:
Vector<u8> m_above_partition_context; Vector<u8> m_above_partition_context;
Vector<u8> m_left_partition_context; Vector<u8> m_left_partition_context;
// FIXME: Move (some?) mi_.. to an array of struct since they are usually used together.
u32 m_mi_row_start { 0 };
u32 m_mi_row_end { 0 };
u32 m_mi_col_start { 0 };
u32 m_mi_col_end { 0 };
BlockSubsize m_mi_size { 0 };
u8 m_segment_id { 0 }; u8 m_segment_id { 0 };
// FIXME: Should this be an enum? // FIXME: Should this be an enum?
// skip equal to 0 indicates that there may be some transform coefficients to read for this block; skip equal to 1 // skip equal to 0 indicates that there may be some transform coefficients to read for this block; skip equal to 1
@ -219,7 +202,6 @@ private:
// skip may be set to 0 even if transform blocks contain immediate end of block markers. // skip may be set to 0 even if transform blocks contain immediate end of block markers.
bool m_skip { false }; bool m_skip { false };
TXSize m_max_tx_size { TX_4x4 }; TXSize m_max_tx_size { TX_4x4 };
BlockSubsize m_block_subsize { BlockSubsize::Block_4x4 };
TXSize m_tx_size { TX_4x4 }; TXSize m_tx_size { TX_4x4 };
ReferenceFramePair m_ref_frame; ReferenceFramePair m_ref_frame;
bool m_is_inter { false }; bool m_is_inter { false };
@ -253,13 +235,6 @@ private:
// FIXME: Use Array<MotionVectorPair, 4> instead. // FIXME: Use Array<MotionVectorPair, 4> instead.
Array<Array<MotionVector, 4>, 2> m_block_mvs; Array<Array<MotionVector, 4>, 2> m_block_mvs;
// FIXME: From spec: NOTE We are using a 2D array to store the SubModes for clarity. It is possible to reduce memory
// consumption by only storing one intra mode for each 8x8 horizontal and vertical position, i.e. to use two 1D
// arrays instead.
// I think should also apply to other fields that are only accessed relative to the current block. Worth looking
// into how much of this context needs to be stored for the whole frame vs a row or column from the current tile.
Vector2D<FrameBlockContext> m_frame_block_contexts;
MotionVectorPair m_candidate_mv; MotionVectorPair m_candidate_mv;
ReferenceFramePair m_candidate_frame; ReferenceFramePair m_candidate_frame;
u8 m_ref_mv_count { 0 }; u8 m_ref_mv_count { 0 };