From a3f80a97a3f82dd739febc2cee496b2016eb282a Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Sun, 1 Aug 2021 22:26:00 -0400 Subject: vp9: Fix reference frame refreshes This resolves the artifacting when decoding VP9 streams. --- src/video_core/command_classes/codecs/vp9.cpp | 77 +++++++++++---------------- src/video_core/command_classes/codecs/vp9.h | 2 +- 2 files changed, 31 insertions(+), 48 deletions(-) (limited to 'src/video_core/command_classes') diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp index 902bc2a98..b3e3462d6 100644 --- a/src/video_core/command_classes/codecs/vp9.cpp +++ b/src/video_core/command_classes/codecs/vp9.cpp @@ -613,7 +613,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() { // Reset context prev_frame_probs = default_probs; - swap_next_golden = false; + swap_ref_indices = false; loop_filter_ref_deltas.fill(0); loop_filter_mode_deltas.fill(0); @@ -626,73 +626,57 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() { // intra only, meaning the frame can be recreated with no other references current_frame_info.intra_only = true; - } else { - if (!current_frame_info.show_frame) { uncomp_writer.WriteBit(current_frame_info.intra_only); - if (!current_frame_info.last_frame_was_key) { - swap_next_golden = !swap_next_golden; - } } else { current_frame_info.intra_only = false; } if (!current_frame_info.error_resilient_mode) { uncomp_writer.WriteU(0, 2); // Reset frame context. } - - // Last, Golden, Altref frames - std::array ref_frame_index{0, 1, 2}; - - // Set when next frame is hidden - // altref and golden references are swapped - if (swap_next_golden) { - ref_frame_index = std::array{0, 2, 1}; + const auto& curr_offsets = current_frame_info.frame_offsets; + const auto& next_offsets = next_frame.info.frame_offsets; + const bool ref_frames_different = curr_offsets[1] != curr_offsets[2]; + const bool next_references_swap = + (next_offsets[1] == curr_offsets[2]) || (next_offsets[2] == curr_offsets[1]); + const bool needs_ref_swap = ref_frames_different && next_references_swap; + if (needs_ref_swap) { + swap_ref_indices = !swap_ref_indices; } - - // update Last Frame - u64 refresh_frame_flags = 1; - - // golden frame may refresh, determined if the next golden frame offset is changed - bool golden_refresh = false; - if (grace_period <= 0) { - for (s32 index = 1; index < 3; ++index) { - if (current_frame_info.frame_offsets[index] != - next_frame.info.frame_offsets[index]) { - current_frame_info.refresh_frame[index] = true; - golden_refresh = true; - grace_period = 3; - } + union { + u32 raw; + BitField<0, 1, u32> refresh_last; + BitField<1, 2, u32> refresh_golden; + BitField<2, 1, u32> refresh_alt; + } refresh_frame_flags; + + refresh_frame_flags.raw = 0; + for (u32 index = 0; index < 3; ++index) { + // Refresh indices that use the current frame as an index + if (curr_offsets[3] == next_offsets[index]) { + refresh_frame_flags.raw |= 1u << index; } } - - if (current_frame_info.show_frame && - (!next_frame.info.show_frame || next_frame.info.is_key_frame)) { - // Update golden frame - refresh_frame_flags = swap_next_golden ? 2 : 4; + if (swap_ref_indices) { + const u32 temp = refresh_frame_flags.refresh_golden; + refresh_frame_flags.refresh_golden.Assign(refresh_frame_flags.refresh_alt.Value()); + refresh_frame_flags.refresh_alt.Assign(temp); } - - if (!current_frame_info.show_frame) { - // Update altref - refresh_frame_flags = swap_next_golden ? 2 : 4; - } else if (golden_refresh) { - refresh_frame_flags = 3; - } - if (current_frame_info.intra_only) { uncomp_writer.WriteU(frame_sync_code, 24); - uncomp_writer.WriteU(static_cast(refresh_frame_flags), 8); + uncomp_writer.WriteU(refresh_frame_flags.raw, 8); uncomp_writer.WriteU(current_frame_info.frame_size.width - 1, 16); uncomp_writer.WriteU(current_frame_info.frame_size.height - 1, 16); uncomp_writer.WriteBit(false); // Render and frame size different. } else { - uncomp_writer.WriteU(static_cast(refresh_frame_flags), 8); - - for (s32 index = 1; index < 4; index++) { + const bool swap_indices = needs_ref_swap ^ swap_ref_indices; + const auto ref_frame_index = swap_indices ? std::array{0, 2, 1} : std::array{0, 1, 2}; + uncomp_writer.WriteU(refresh_frame_flags.raw, 8); + for (size_t index = 1; index < 4; index++) { uncomp_writer.WriteU(ref_frame_index[index - 1], 3); uncomp_writer.WriteU(current_frame_info.ref_frame_sign_bias[index], 1); } - uncomp_writer.WriteBit(true); // Frame size with refs. uncomp_writer.WriteBit(false); // Render and frame size different. uncomp_writer.WriteBit(current_frame_info.allow_high_precision_mv); @@ -812,7 +796,6 @@ const std::vector& VP9::ComposeFrameHeader(const NvdecCommon::NvdecRegisters current_frame_info = curr_frame.info; bitstream = std::move(curr_frame.bit_stream); } - // The uncompressed header routine sets PrevProb parameters needed for the compressed header auto uncomp_writer = ComposeUncompressedHeader(); std::vector compressed_header = ComposeCompressedHeader(); diff --git a/src/video_core/command_classes/codecs/vp9.h b/src/video_core/command_classes/codecs/vp9.h index 8396c8105..bb4d0d972 100644 --- a/src/video_core/command_classes/codecs/vp9.h +++ b/src/video_core/command_classes/codecs/vp9.h @@ -184,7 +184,7 @@ private: std::array frame_ctxs{}; Vp9FrameContainer next_frame{}; Vp9FrameContainer next_next_frame{}; - bool swap_next_golden{}; + bool swap_ref_indices{}; Vp9PictureInfo current_frame_info{}; Vp9EntropyProbs prev_frame_probs{}; -- cgit v1.2.3 From e3688f0627aa460bffb9b79c7a45e4ed615a1fa7 Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Mon, 2 Aug 2021 00:08:41 -0400 Subject: vp9: Cleanup unused variables With reference frames refreshes fix, we no longer need to buffer two frames in advance. We can also remove other unused or otherwise unneeded variables. --- src/video_core/command_classes/codecs/vp9.cpp | 57 ++++++----------------- src/video_core/command_classes/codecs/vp9.h | 12 +---- src/video_core/command_classes/codecs/vp9_types.h | 6 --- 3 files changed, 17 insertions(+), 58 deletions(-) (limited to 'src/video_core/command_classes') diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp index b3e3462d6..7eecb3991 100644 --- a/src/video_core/command_classes/codecs/vp9.cpp +++ b/src/video_core/command_classes/codecs/vp9.cpp @@ -11,6 +11,9 @@ namespace Tegra::Decoder { namespace { +constexpr u32 diff_update_probability = 252; +constexpr u32 frame_sync_code = 0x498342; + // Default compressed header probabilities once frame context resets constexpr Vp9EntropyProbs default_probs{ .y_mode_prob{ @@ -361,8 +364,7 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy); // surface_luma_offset[0:3] contains the address of the reference frame offsets in the following - // order: last, golden, altref, current. It may be worthwhile to track the updates done here - // to avoid buffering frame data needed for reference frame updating in the header composition. + // order: last, golden, altref, current. std::copy(state.surface_luma_offset.begin(), state.surface_luma_offset.begin() + 4, vp9_info.frame_offsets.begin()); @@ -384,33 +386,18 @@ Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state) gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(), current_frame.info.bitstream_size); } - // Buffer two frames, saving the last show frame info - if (!next_next_frame.bit_stream.empty()) { + if (!next_frame.bit_stream.empty()) { Vp9FrameContainer temp{ .info = current_frame.info, .bit_stream = std::move(current_frame.bit_stream), }; - next_next_frame.info.show_frame = current_frame.info.last_frame_shown; - current_frame.info = next_next_frame.info; - current_frame.bit_stream = std::move(next_next_frame.bit_stream); - next_next_frame = std::move(temp); - - if (!next_frame.bit_stream.empty()) { - Vp9FrameContainer temp2{ - .info = current_frame.info, - .bit_stream = std::move(current_frame.bit_stream), - }; - next_frame.info.show_frame = current_frame.info.last_frame_shown; - current_frame.info = next_frame.info; - current_frame.bit_stream = std::move(next_frame.bit_stream); - next_frame = std::move(temp2); - } else { - next_frame.info = current_frame.info; - next_frame.bit_stream = std::move(current_frame.bit_stream); - } + next_frame.info.show_frame = current_frame.info.last_frame_shown; + current_frame.info = next_frame.info; + current_frame.bit_stream = std::move(next_frame.bit_stream); + next_frame = std::move(temp); } else { - next_next_frame.info = current_frame.info; - next_next_frame.bit_stream = std::move(current_frame.bit_stream); + next_frame.info = current_frame.info; + next_frame.bit_stream = std::move(current_frame.bit_stream); } return current_frame; } @@ -616,13 +603,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() { swap_ref_indices = false; loop_filter_ref_deltas.fill(0); loop_filter_mode_deltas.fill(0); - - // allow frames offsets to stabilize before checking for golden frames - grace_period = 4; - - // On key frames, all frame slots are set to the current frame, - // so the value of the selected slot doesn't really matter. - frame_ctxs.fill({current_frame_number, false, default_probs}); + frame_ctxs.fill(default_probs); // intra only, meaning the frame can be recreated with no other references current_frame_info.intra_only = true; @@ -698,10 +679,9 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() { frame_ctx_idx = 1; } - uncomp_writer.WriteU(frame_ctx_idx, 2); // Frame context index. - prev_frame_probs = - frame_ctxs[frame_ctx_idx].probs; // reference probabilities for compressed header - frame_ctxs[frame_ctx_idx] = {current_frame_number, false, current_frame_info.entropy}; + uncomp_writer.WriteU(frame_ctx_idx, 2); // Frame context index. + prev_frame_probs = frame_ctxs[frame_ctx_idx]; // reference probabilities for compressed header + frame_ctxs[frame_ctx_idx] = current_frame_info.entropy; uncomp_writer.WriteU(current_frame_info.first_level, 6); uncomp_writer.WriteU(current_frame_info.sharpness_level, 3); @@ -811,13 +791,6 @@ const std::vector& VP9::ComposeFrameHeader(const NvdecCommon::NvdecRegisters frame.begin() + uncompressed_header.size()); std::copy(bitstream.begin(), bitstream.end(), frame.begin() + uncompressed_header.size() + compressed_header.size()); - - // keep track of frame number - current_frame_number++; - grace_period--; - - // don't display hidden frames - hidden = !current_frame_info.show_frame; return frame; } diff --git a/src/video_core/command_classes/codecs/vp9.h b/src/video_core/command_classes/codecs/vp9.h index bb4d0d972..e6e9fc17e 100644 --- a/src/video_core/command_classes/codecs/vp9.h +++ b/src/video_core/command_classes/codecs/vp9.h @@ -14,7 +14,6 @@ namespace Tegra { class GPU; -enum class FrameType { KeyFrame = 0, InterFrame = 1 }; namespace Decoder { /// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the @@ -124,7 +123,7 @@ public: /// Returns true if the most recent frame was a hidden frame. [[nodiscard]] bool WasFrameHidden() const { - return hidden; + return !current_frame_info.show_frame; } private: @@ -178,19 +177,12 @@ private: std::array loop_filter_ref_deltas{}; std::array loop_filter_mode_deltas{}; - bool hidden = false; - s64 current_frame_number = -2; // since we buffer 2 frames - s32 grace_period = 6; // frame offsets need to stabilize - std::array frame_ctxs{}; Vp9FrameContainer next_frame{}; - Vp9FrameContainer next_next_frame{}; + std::array frame_ctxs{}; bool swap_ref_indices{}; Vp9PictureInfo current_frame_info{}; Vp9EntropyProbs prev_frame_probs{}; - - s32 diff_update_probability = 252; - s32 frame_sync_code = 0x498342; }; } // namespace Decoder diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h index 2da14f3ca..6820afa26 100644 --- a/src/video_core/command_classes/codecs/vp9_types.h +++ b/src/video_core/command_classes/codecs/vp9_types.h @@ -296,12 +296,6 @@ struct RefPoolElement { bool refresh{}; }; -struct FrameContexts { - s64 from; - bool adapted; - Vp9EntropyProbs probs; -}; - #define ASSERT_POSITION(field_name, position) \ static_assert(offsetof(Vp9EntropyProbs, field_name) == position, \ "Field " #field_name " has invalid position") -- cgit v1.2.3