|
|
|
@ -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; |
|
|
|
} |
|
|
|
@ -613,86 +600,64 @@ 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); |
|
|
|
|
|
|
|
// 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; |
|
|
|
|
|
|
|
} 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<s32, 3> 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<s32, 3>{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 (!current_frame_info.show_frame) { |
|
|
|
// Update altref
|
|
|
|
refresh_frame_flags = swap_next_golden ? 2 : 4; |
|
|
|
} else if (golden_refresh) { |
|
|
|
refresh_frame_flags = 3; |
|
|
|
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.intra_only) { |
|
|
|
uncomp_writer.WriteU(frame_sync_code, 24); |
|
|
|
uncomp_writer.WriteU(static_cast<s32>(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<s32>(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); |
|
|
|
@ -714,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); |
|
|
|
@ -812,7 +776,6 @@ const std::vector<u8>& 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<u8> compressed_header = ComposeCompressedHeader(); |
|
|
|
@ -828,13 +791,6 @@ const std::vector<u8>& 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; |
|
|
|
} |
|
|
|
|
|
|
|
|