From 7f46033c012d7fa5971674279381c60255fc5eeb Mon Sep 17 00:00:00 2001 From: Zaggy1024 Date: Sat, 8 Oct 2022 19:55:24 -0500 Subject: [PATCH] LibVideo: Cache 64 bits at a time for reading in BitStream Reads will now be done in larger chunks at a time. The public read_byte() function was removed in favor of a private fill_reservoir() function which will be used to fill the 64-bit reservoir field which will then be bit-shifted and masked as necessary for subsequent arbitrary bit-sized reads. read_f(n) was renamed to read_bits to be clearer about its use. --- Userland/Libraries/LibVideo/VP9/BitStream.cpp | 91 ++++++++++--------- Userland/Libraries/LibVideo/VP9/BitStream.h | 16 ++-- Userland/Libraries/LibVideo/VP9/Parser.cpp | 22 ++--- 3 files changed, 69 insertions(+), 60 deletions(-) diff --git a/Userland/Libraries/LibVideo/VP9/BitStream.cpp b/Userland/Libraries/LibVideo/VP9/BitStream.cpp index 6d65e123d4..ffade4ef02 100644 --- a/Userland/Libraries/LibVideo/VP9/BitStream.cpp +++ b/Userland/Libraries/LibVideo/VP9/BitStream.cpp @@ -5,60 +5,67 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include - #include "BitStream.h" namespace Video::VP9 { -ErrorOr BitStream::read_byte() +ErrorOr BitStream::fill_reservoir() { - if (m_bytes_remaining < 1) - return Error::from_string_literal("read_byte: Out of data."); - VERIFY(m_bytes_remaining >= 1); - m_bytes_remaining--; - return *(m_data_ptr++); + VERIFY(m_reservoir_bits_remaining == 0); + if (m_data_ptr == m_end_ptr) + return Error::from_string_literal("Stream is out of data"); + VERIFY(m_data_ptr < m_end_ptr); + m_reservoir = 0; + + size_t byte_index; + for (byte_index = 0; m_data_ptr < m_end_ptr && byte_index < sizeof(m_reservoir); byte_index++) { + m_reservoir = (m_reservoir << 8) | *m_data_ptr; + m_data_ptr++; + } + + m_reservoir_bits_remaining = byte_index * 8; + m_reservoir <<= (sizeof(m_reservoir) - byte_index) * 8; + return {}; +} + +ErrorOr BitStream::read_bits(u8 bit_count) +{ + if (bit_count > sizeof(u64) * 8) + return Error::from_string_literal("Requested read is too large"); + u64 result = 0; + + while (bit_count > 0) { + if (m_reservoir_bits_remaining == 0) + TRY(fill_reservoir()); + + u64 batch_bits = min(bit_count, m_reservoir_bits_remaining); + u64 bit_shift = (sizeof(m_reservoir) * 8u) - batch_bits; + + result = (result << batch_bits) | m_reservoir >> bit_shift; + m_reservoir <<= batch_bits; + bit_count -= batch_bits; + m_reservoir_bits_remaining -= batch_bits; + m_bits_read += batch_bits; + } + + return result; } ErrorOr BitStream::read_bit() { - if (!m_current_byte.has_value()) { - m_current_byte = TRY(read_byte()); - m_current_bit_position = 7; - } - - bool bit_value = m_current_byte.value() & (1u << m_current_bit_position); - if (--m_current_bit_position < 0) - m_current_byte.clear(); - return bit_value; -} - -ErrorOr BitStream::read_f(size_t n) -{ - u8 result = 0; - for (size_t i = 0; i < n; i++) { - result = (2 * result) + TRY(read_bit()); - } - return result; + auto value = TRY(read_bits(1)); + VERIFY(value <= 2); + return value != 0; } ErrorOr BitStream::read_f8() { - if (!m_current_byte.has_value()) - return read_byte(); - - auto high_bits = m_current_byte.value() & ((1u << m_current_bit_position) - 1); - u8 remaining_bits = 7 - m_current_bit_position; - m_current_byte = TRY(read_byte()); - m_current_bit_position = 7; - auto low_bits = (m_current_byte.value() >> (8u - remaining_bits)) & ((1u << remaining_bits) - 1); - m_current_bit_position -= remaining_bits; - return (high_bits << remaining_bits) | low_bits; + return TRY(read_bits(8)); } ErrorOr BitStream::read_f16() { - return (TRY(read_f8()) << 8u) | TRY(read_f8()); + return TRY(read_bits(16)); } /* 9.2.1 */ @@ -108,7 +115,7 @@ ErrorOr BitStream::read_bool(u8 probability) ErrorOr BitStream::exit_bool() { // FIXME: I'm not sure if this call to min is spec compliant, or if there is an issue elsewhere earlier in the parser. - auto padding_element = TRY(read_f(min(m_bool_max_bits, (u64)bits_remaining()))); + auto padding_element = TRY(read_bits(min(m_bool_max_bits, (u64)bits_remaining()))); // FIXME: It is a requirement of bitstream conformance that enough padding bits are inserted to ensure that the final coded byte of a frame is not equal to a superframe marker. // A byte b is equal to a superframe marker if and only if (b & 0xe0)is equal to 0xc0, i.e. if the most significant 3 bits are equal to 0b110. @@ -128,24 +135,24 @@ ErrorOr BitStream::read_literal(size_t n) ErrorOr BitStream::read_s(size_t n) { - auto value = TRY(read_f(n)); + auto value = TRY(read_bits(n)); auto sign = TRY(read_bit()); return sign ? -value : value; } u64 BitStream::get_position() { - return (m_bytes_read * 8) + (7 - m_current_bit_position); + return m_bits_read; } size_t BitStream::bytes_remaining() { - return m_bytes_remaining; + return (m_end_ptr - m_data_ptr) + (m_reservoir_bits_remaining / 8); } size_t BitStream::bits_remaining() { - return (bytes_remaining() * 8) + m_current_bit_position + 1; + return ((m_end_ptr - m_data_ptr) * sizeof(m_data_ptr) * 8) + m_reservoir_bits_remaining; } } diff --git a/Userland/Libraries/LibVideo/VP9/BitStream.h b/Userland/Libraries/LibVideo/VP9/BitStream.h index 6141386177..0085d974b3 100644 --- a/Userland/Libraries/LibVideo/VP9/BitStream.h +++ b/Userland/Libraries/LibVideo/VP9/BitStream.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -16,15 +17,14 @@ class BitStream { public: BitStream(u8 const* data, size_t size) : m_data_ptr(data) - , m_bytes_remaining(size) + , m_end_ptr(data + size) { } - ErrorOr read_byte(); ErrorOr read_bit(); + ErrorOr read_bits(u8 bit_count); /* (9.1) */ - ErrorOr read_f(size_t n); ErrorOr read_f8(); ErrorOr read_f16(); @@ -42,11 +42,13 @@ public: size_t bits_remaining(); private: + ErrorOr fill_reservoir(); + u8 const* m_data_ptr { nullptr }; - size_t m_bytes_remaining { 0 }; - Optional m_current_byte; - i8 m_current_bit_position { 0 }; - u64 m_bytes_read { 0 }; + u8 const* m_end_ptr { nullptr }; + u64 m_reservoir; + u8 m_reservoir_bits_remaining { 0 }; + size_t m_bits_read { 0 }; u8 m_bool_value { 0 }; u8 m_bool_range { 0 }; diff --git a/Userland/Libraries/LibVideo/VP9/Parser.cpp b/Userland/Libraries/LibVideo/VP9/Parser.cpp index 7e2b505c82..69e3df50f6 100644 --- a/Userland/Libraries/LibVideo/VP9/Parser.cpp +++ b/Userland/Libraries/LibVideo/VP9/Parser.cpp @@ -95,7 +95,7 @@ ErrorOr Parser::refresh_probs() /* (6.2) */ ErrorOr Parser::uncompressed_header() { - auto frame_marker = TRY(m_bit_stream->read_f(2)); + auto frame_marker = TRY(m_bit_stream->read_bits(2)); if (frame_marker != 2) return Error::from_string_literal("uncompressed_header: Frame marker must be 2"); auto profile_low_bit = TRY(m_bit_stream->read_bit()); @@ -105,7 +105,7 @@ ErrorOr Parser::uncompressed_header() return Error::from_string_literal("uncompressed_header: Profile 3 reserved bit was non-zero"); auto show_existing_frame = TRY(m_bit_stream->read_bit()); if (show_existing_frame) { - m_frame_to_show_map_index = TRY(m_bit_stream->read_f(3)); + m_frame_to_show_map_index = TRY(m_bit_stream->read_bits(3)); m_header_size_in_bytes = 0; m_refresh_frame_flags = 0; m_loop_filter_level = 0; @@ -128,7 +128,7 @@ ErrorOr Parser::uncompressed_header() m_frame_is_intra = !m_show_frame && TRY(m_bit_stream->read_bit()); if (!m_error_resilient_mode) { - m_reset_frame_context = TRY(m_bit_stream->read_f(2)); + m_reset_frame_context = TRY(m_bit_stream->read_bits(2)); } else { m_reset_frame_context = 0; } @@ -150,7 +150,7 @@ ErrorOr Parser::uncompressed_header() } else { m_refresh_frame_flags = TRY(m_bit_stream->read_f8()); for (auto i = 0; i < 3; i++) { - m_ref_frame_idx[i] = TRY(m_bit_stream->read_f(3)); + m_ref_frame_idx[i] = TRY(m_bit_stream->read_bits(3)); m_ref_frame_sign_bias[LastFrame + i] = TRY(m_bit_stream->read_bit()); } TRY(frame_size_with_refs()); @@ -167,7 +167,7 @@ ErrorOr Parser::uncompressed_header() m_frame_parallel_decoding_mode = true; } - m_frame_context_idx = TRY(m_bit_stream->read_f(2)); + m_frame_context_idx = TRY(m_bit_stream->read_bits(2)); if (m_frame_is_intra || m_error_resilient_mode) { setup_past_independence(); if (m_frame_type == KeyFrame || m_error_resilient_mode || m_reset_frame_context == 3) { @@ -209,7 +209,7 @@ ErrorOr Parser::color_config() m_bit_depth = 8; } - auto color_space = TRY(m_bit_stream->read_f(3)); + auto color_space = TRY(m_bit_stream->read_bits(3)); VERIFY(color_space <= RGB); m_color_space = static_cast(color_space); @@ -291,15 +291,15 @@ ErrorOr Parser::read_interpolation_filter() if (TRY(m_bit_stream->read_bit())) { m_interpolation_filter = Switchable; } else { - m_interpolation_filter = literal_to_type[TRY(m_bit_stream->read_f(2))]; + m_interpolation_filter = literal_to_type[TRY(m_bit_stream->read_bits(2))]; } return {}; } ErrorOr Parser::loop_filter_params() { - m_loop_filter_level = TRY(m_bit_stream->read_f(6)); - m_loop_filter_sharpness = TRY(m_bit_stream->read_f(3)); + m_loop_filter_level = TRY(m_bit_stream->read_bits(6)); + m_loop_filter_sharpness = TRY(m_bit_stream->read_bits(3)); m_loop_filter_delta_enabled = TRY(m_bit_stream->read_bit()); if (m_loop_filter_delta_enabled) { if (TRY(m_bit_stream->read_bit())) { @@ -362,7 +362,7 @@ ErrorOr Parser::segmentation_params() m_feature_enabled[i][j] = feature_enabled; if (feature_enabled) { auto bits_to_read = segmentation_feature_bits[j]; - feature_value = TRY(m_bit_stream->read_f(bits_to_read)); + feature_value = TRY(m_bit_stream->read_bits(bits_to_read)); if (segmentation_feature_signed[j]) { if (TRY(m_bit_stream->read_bit())) feature_value = -feature_value; @@ -764,7 +764,7 @@ ErrorOr Parser::decode_tiles() if (last_tile) tile_size = m_bit_stream->bytes_remaining(); else - tile_size = TRY(m_bit_stream->read_f(32)); + tile_size = TRY(m_bit_stream->read_bits(32)); m_mi_row_start = get_tile_offset(tile_row, m_mi_rows, m_tile_rows_log2); m_mi_row_end = get_tile_offset(tile_row + 1, m_mi_rows, m_tile_rows_log2);