aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/host1x/codecs
diff options
context:
space:
mode:
authordarktux <darktux@y2nlvhmmk5jnsvechppxnbyzmmv3vbl7dvzn6ltwcdbpgxixp3clkgqd.onion>2024-04-05 01:58:29 +0200
committerBriar <205427297+icybriarr@users.noreply.github.com>2025-04-04 03:40:46 +0200
commit616619c8f754dc1a783d91aa5b32f45e8df74172 (patch)
tree4aa2bdd001cbf38b58439f59e62ee7705af6a253 /src/video_core/host1x/codecs
parent19a93a3fd1dd6a7db074d254990db073d62b08e3 (diff)
Port changes from Early Access
Diffstat (limited to 'src/video_core/host1x/codecs')
-rwxr-xr-xsrc/video_core/host1x/codecs/decoder.cpp71
-rwxr-xr-xsrc/video_core/host1x/codecs/decoder.h64
-rw-r--r--src/video_core/host1x/codecs/h264.cpp147
-rw-r--r--src/video_core/host1x/codecs/h264.h299
-rw-r--r--src/video_core/host1x/codecs/vp8.cpp73
-rw-r--r--src/video_core/host1x/codecs/vp8.h44
-rw-r--r--src/video_core/host1x/codecs/vp9.cpp152
-rw-r--r--src/video_core/host1x/codecs/vp9.h46
-rw-r--r--src/video_core/host1x/codecs/vp9_types.h27
9 files changed, 685 insertions, 238 deletions
diff --git a/src/video_core/host1x/codecs/decoder.cpp b/src/video_core/host1x/codecs/decoder.cpp
new file mode 100755
index 0000000000..49a601969c
--- /dev/null
+++ b/src/video_core/host1x/codecs/decoder.cpp
@@ -0,0 +1,71 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/assert.h"
+#include "common/settings.h"
+#include "video_core/host1x/codecs/decoder.h"
+#include "video_core/host1x/host1x.h"
+#include "video_core/memory_manager.h"
+
+namespace Tegra {
+
+Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::NvdecRegisters& regs_,
+ Host1x::FrameQueue& frame_queue_)
+ : host1x(host1x_), memory_manager{host1x.GMMU()}, regs{regs_}, id{id_}, frame_queue{
+ frame_queue_} {}
+
+Decoder::~Decoder() = default;
+
+void Decoder::Decode() {
+ if (!initialized) {
+ return;
+ }
+
+ const auto packet_data = ComposeFrame();
+ // Send assembled bitstream to decoder.
+ if (!decode_api.SendPacket(packet_data)) {
+ return;
+ }
+
+ // Only receive/store visible frames.
+ if (vp9_hidden_frame) {
+ return;
+ }
+
+ // Receive output frames from decoder.
+ auto frame = decode_api.ReceiveFrame();
+
+ if (IsInterlaced()) {
+ auto [luma_top, luma_bottom, chroma_top, chroma_bottom] = GetInterlacedOffsets();
+ auto frame_copy = frame;
+
+ if (!frame.get()) {
+ LOG_ERROR(HW_GPU,
+ "Nvdec {} dailed to decode interlaced frame for top 0x{:X} bottom 0x{:X}", id,
+ luma_top, luma_bottom);
+ }
+
+ if (UsingDecodeOrder()) {
+ frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
+ frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy));
+ } else {
+ frame_queue.PushPresentOrder(id, luma_top, std::move(frame));
+ frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy));
+ }
+ } else {
+ auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
+
+ if (!frame.get()) {
+ LOG_ERROR(HW_GPU, "Nvdec {} failed to decode progressive frame for luma 0x{:X}", id,
+ luma_offset);
+ }
+
+ if (UsingDecodeOrder()) {
+ frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame));
+ } else {
+ frame_queue.PushPresentOrder(id, luma_offset, std::move(frame));
+ }
+ }
+}
+
+} // namespace Tegra
diff --git a/src/video_core/host1x/codecs/decoder.h b/src/video_core/host1x/codecs/decoder.h
new file mode 100755
index 0000000000..22e6db8151
--- /dev/null
+++ b/src/video_core/host1x/codecs/decoder.h
@@ -0,0 +1,64 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string_view>
+#include <unordered_map>
+#include <queue>
+
+#include "common/common_types.h"
+#include "video_core/host1x/ffmpeg/ffmpeg.h"
+#include "video_core/host1x/nvdec_common.h"
+
+namespace Tegra {
+
+namespace Host1x {
+class Host1x;
+class FrameQueue;
+} // namespace Host1x
+
+class Decoder {
+public:
+ virtual ~Decoder();
+
+ /// Call decoders to construct headers, decode AVFrame with ffmpeg
+ void Decode();
+
+ bool UsingDecodeOrder() const {
+ return decode_api.UsingDecodeOrder();
+ }
+
+ /// Returns the value of current_codec
+ [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const {
+ return codec;
+ }
+
+ /// Return name of the current codec
+ [[nodiscard]] virtual std::string_view GetCurrentCodecName() const = 0;
+
+protected:
+ explicit Decoder(Host1x::Host1x& host1x, s32 id,
+ const Host1x::NvdecCommon::NvdecRegisters& regs,
+ Host1x::FrameQueue& frame_queue);
+
+ virtual std::span<const u8> ComposeFrame() = 0;
+ virtual std::tuple<u64, u64> GetProgressiveOffsets() = 0;
+ virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0;
+ virtual bool IsInterlaced() = 0;
+
+ Host1x::Host1x& host1x;
+ Tegra::MemoryManager& memory_manager;
+ const Host1x::NvdecCommon::NvdecRegisters& regs;
+ s32 id;
+ Host1x::FrameQueue& frame_queue;
+ Host1x::NvdecCommon::VideoCodec codec;
+ FFmpeg::DecodeApi decode_api;
+ bool initialized{};
+ bool vp9_hidden_frame{};
+};
+
+} // namespace Tegra
diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp
index 994591c8dc..782d11d725 100644
--- a/src/video_core/host1x/codecs/h264.cpp
+++ b/src/video_core/host1x/codecs/h264.cpp
@@ -1,5 +1,5 @@
-// SPDX-FileCopyrightText: Ryujinx Team and Contributors
-// SPDX-License-Identifier: MIT
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <bit>
@@ -10,7 +10,7 @@
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
-namespace Tegra::Decoder {
+namespace Tegra::Decoders {
namespace {
// ZigZag LUTs from libavcodec.
constexpr std::array<u8, 64> zig_zag_direct{
@@ -25,23 +25,56 @@ constexpr std::array<u8, 16> zig_zag_scan{
};
} // Anonymous namespace
-H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {}
+H264::H264(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_,
+ Host1x::FrameQueue& frame_queue_)
+ : Decoder{host1x_, id_, regs_, frame_queue_} {
+ codec = Host1x::NvdecCommon::VideoCodec::H264;
+ initialized = decode_api.Initialize(codec);
+}
H264::~H264() = default;
-std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
- size_t* out_configuration_size, bool is_first_frame) {
- H264DecoderContext context;
- host1x.GMMU().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext));
+std::tuple<u64, u64> H264::GetProgressiveOffsets() {
+ auto pic_idx{current_context.h264_parameter_set.curr_pic_idx};
+ auto luma{regs.surface_luma_offsets[pic_idx].Address() +
+ current_context.h264_parameter_set.luma_frame_offset.Address()};
+ auto chroma{regs.surface_chroma_offsets[pic_idx].Address() +
+ current_context.h264_parameter_set.chroma_frame_offset.Address()};
+ return {luma, chroma};
+}
+
+std::tuple<u64, u64, u64, u64> H264::GetInterlacedOffsets() {
+ auto pic_idx{current_context.h264_parameter_set.curr_pic_idx};
+ auto luma_top{regs.surface_luma_offsets[pic_idx].Address() +
+ current_context.h264_parameter_set.luma_top_offset.Address()};
+ auto luma_bottom{regs.surface_luma_offsets[pic_idx].Address() +
+ current_context.h264_parameter_set.luma_bot_offset.Address()};
+ auto chroma_top{regs.surface_chroma_offsets[pic_idx].Address() +
+ current_context.h264_parameter_set.chroma_top_offset.Address()};
+ auto chroma_bottom{regs.surface_chroma_offsets[pic_idx].Address() +
+ current_context.h264_parameter_set.chroma_bot_offset.Address()};
+ return {luma_top, luma_bottom, chroma_top, chroma_bottom};
+}
+
+bool H264::IsInterlaced() {
+ return current_context.h264_parameter_set.luma_top_offset.Address() != 0 ||
+ current_context.h264_parameter_set.luma_bot_offset.Address() != 0;
+}
+
+std::span<const u8> H264::ComposeFrame() {
+ memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context,
+ sizeof(H264DecoderContext));
- const s64 frame_number = context.h264_parameter_set.frame_number.Value();
+ const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
if (!is_first_frame && frame_number != 0) {
- frame.resize_destructive(context.stream_len);
- host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
- *out_configuration_size = 0;
- return frame;
+ frame_scratch.resize_destructive(current_context.stream_len);
+ memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(), frame_scratch.data(),
+ frame_scratch.size());
+ return frame_scratch;
}
+ is_first_frame = false;
+
// Encode header
H264BitWriter writer{};
writer.WriteU(1, 24);
@@ -53,7 +86,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
writer.WriteU(31, 8);
writer.WriteUe(0);
const u32 chroma_format_idc =
- static_cast<u32>(context.h264_parameter_set.chroma_format_idc.Value());
+ static_cast<u32>(current_context.h264_parameter_set.chroma_format_idc.Value());
writer.WriteUe(chroma_format_idc);
if (chroma_format_idc == 3) {
writer.WriteBit(false);
@@ -61,42 +94,44 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
writer.WriteUe(0);
writer.WriteUe(0);
- writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
+ writer.WriteBit(current_context.qpprime_y_zero_transform_bypass_flag.Value() != 0);
writer.WriteBit(false); // Scaling matrix present flag
- writer.WriteUe(static_cast<u32>(context.h264_parameter_set.log2_max_frame_num_minus4.Value()));
+ writer.WriteUe(
+ static_cast<u32>(current_context.h264_parameter_set.log2_max_frame_num_minus4.Value()));
const auto order_cnt_type =
- static_cast<u32>(context.h264_parameter_set.pic_order_cnt_type.Value());
+ static_cast<u32>(current_context.h264_parameter_set.pic_order_cnt_type.Value());
writer.WriteUe(order_cnt_type);
if (order_cnt_type == 0) {
- writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt_lsb_minus4);
+ writer.WriteUe(current_context.h264_parameter_set.log2_max_pic_order_cnt_lsb_minus4);
} else if (order_cnt_type == 1) {
- writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
+ writer.WriteBit(current_context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
writer.WriteSe(0);
writer.WriteSe(0);
writer.WriteUe(0);
}
- const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units /
- (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
+ const s32 pic_height = current_context.h264_parameter_set.frame_height_in_mbs /
+ (current_context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
- // TODO (ameerj): Where do we get this number, it seems to be particular for each stream
- const auto nvdec_decoding = Settings::values.nvdec_emulation.GetValue();
- const bool uses_gpu_decoding = nvdec_decoding == Settings::NvdecEmulation::Gpu;
- const u32 max_num_ref_frames = uses_gpu_decoding ? 6u : 16u;
+ u32 max_num_ref_frames =
+ std::max(std::max(current_context.h264_parameter_set.num_refidx_l0_default_active,
+ current_context.h264_parameter_set.num_refidx_l1_default_active) +
+ 1,
+ 4);
writer.WriteUe(max_num_ref_frames);
writer.WriteBit(false);
- writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
+ writer.WriteUe(current_context.h264_parameter_set.pic_width_in_mbs - 1);
writer.WriteUe(pic_height - 1);
- writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0);
+ writer.WriteBit(current_context.h264_parameter_set.frame_mbs_only_flag != 0);
- if (!context.h264_parameter_set.frame_mbs_only_flag) {
- writer.WriteBit(context.h264_parameter_set.flags.mbaff_frame.Value() != 0);
+ if (!current_context.h264_parameter_set.frame_mbs_only_flag) {
+ writer.WriteBit(current_context.h264_parameter_set.flags.mbaff_frame.Value() != 0);
}
- writer.WriteBit(context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0);
+ writer.WriteBit(current_context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0);
writer.WriteBit(false); // Frame cropping flag
writer.WriteBit(false); // VUI parameter present flag
@@ -111,57 +146,59 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
writer.WriteUe(0);
writer.WriteUe(0);
- writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
- writer.WriteBit(context.h264_parameter_set.pic_order_present_flag != 0);
+ writer.WriteBit(current_context.h264_parameter_set.entropy_coding_mode_flag != 0);
+ writer.WriteBit(current_context.h264_parameter_set.pic_order_present_flag != 0);
writer.WriteUe(0);
- writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
- writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active);
- writer.WriteBit(context.h264_parameter_set.flags.weighted_pred.Value() != 0);
- writer.WriteU(static_cast<s32>(context.h264_parameter_set.weighted_bipred_idc.Value()), 2);
- s32 pic_init_qp = static_cast<s32>(context.h264_parameter_set.pic_init_qp_minus26.Value());
+ writer.WriteUe(current_context.h264_parameter_set.num_refidx_l0_default_active);
+ writer.WriteUe(current_context.h264_parameter_set.num_refidx_l1_default_active);
+ writer.WriteBit(current_context.h264_parameter_set.flags.weighted_pred.Value() != 0);
+ writer.WriteU(static_cast<s32>(current_context.h264_parameter_set.weighted_bipred_idc.Value()),
+ 2);
+ s32 pic_init_qp =
+ static_cast<s32>(current_context.h264_parameter_set.pic_init_qp_minus26.Value());
writer.WriteSe(pic_init_qp);
writer.WriteSe(0);
s32 chroma_qp_index_offset =
- static_cast<s32>(context.h264_parameter_set.chroma_qp_index_offset.Value());
+ static_cast<s32>(current_context.h264_parameter_set.chroma_qp_index_offset.Value());
writer.WriteSe(chroma_qp_index_offset);
- writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_present_flag != 0);
- writer.WriteBit(context.h264_parameter_set.flags.constrained_intra_pred.Value() != 0);
- writer.WriteBit(context.h264_parameter_set.redundant_pic_cnt_present_flag != 0);
- writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
+ writer.WriteBit(current_context.h264_parameter_set.deblocking_filter_control_present_flag != 0);
+ writer.WriteBit(current_context.h264_parameter_set.flags.constrained_intra_pred.Value() != 0);
+ writer.WriteBit(current_context.h264_parameter_set.redundant_pic_cnt_present_flag != 0);
+ writer.WriteBit(current_context.h264_parameter_set.transform_8x8_mode_flag != 0);
writer.WriteBit(true); // pic_scaling_matrix_present_flag
for (s32 index = 0; index < 6; index++) {
writer.WriteBit(true);
- std::span<const u8> matrix{context.weight_scale};
- writer.WriteScalingList(scan, matrix, index * 16, 16);
+ std::span<const u8> matrix{current_context.weight_scale_4x4};
+ writer.WriteScalingList(scan_scratch, matrix, index * 16, 16);
}
- if (context.h264_parameter_set.transform_8x8_mode_flag) {
+ if (current_context.h264_parameter_set.transform_8x8_mode_flag) {
for (s32 index = 0; index < 2; index++) {
writer.WriteBit(true);
- std::span<const u8> matrix{context.weight_scale_8x8};
- writer.WriteScalingList(scan, matrix, index * 64, 64);
+ std::span<const u8> matrix{current_context.weight_scale_8x8};
+ writer.WriteScalingList(scan_scratch, matrix, index * 64, 64);
}
}
s32 chroma_qp_index_offset2 =
- static_cast<s32>(context.h264_parameter_set.second_chroma_qp_index_offset.Value());
+ static_cast<s32>(current_context.h264_parameter_set.second_chroma_qp_index_offset.Value());
writer.WriteSe(chroma_qp_index_offset2);
writer.End();
const auto& encoded_header = writer.GetByteArray();
- frame.resize(encoded_header.size() + context.stream_len);
- std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
+ frame_scratch.resize(encoded_header.size() + current_context.stream_len);
+ std::memcpy(frame_scratch.data(), encoded_header.data(), encoded_header.size());
- *out_configuration_size = encoded_header.size();
- host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data() + encoded_header.size(),
- context.stream_len);
+ memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(),
+ frame_scratch.data() + encoded_header.size(),
+ current_context.stream_len);
- return frame;
+ return frame_scratch;
}
H264BitWriter::H264BitWriter() = default;
@@ -278,4 +315,4 @@ void H264BitWriter::Flush() {
buffer = 0;
buffer_pos = 0;
}
-} // namespace Tegra::Decoder
+} // namespace Tegra::Decoders
diff --git a/src/video_core/host1x/codecs/h264.h b/src/video_core/host1x/codecs/h264.h
index 1deaf46320..d946c6937d 100644
--- a/src/video_core/host1x/codecs/h264.h
+++ b/src/video_core/host1x/codecs/h264.h
@@ -1,5 +1,5 @@
-// SPDX-FileCopyrightText: Ryujinx Team and Contributors
-// SPDX-License-Identifier: MIT
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,6 +10,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/scratch_buffer.h"
+#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/nvdec_common.h"
namespace Tegra {
@@ -18,7 +19,7 @@ namespace Host1x {
class Host1x;
} // namespace Host1x
-namespace Decoder {
+namespace Decoders {
class H264BitWriter {
public:
@@ -60,123 +61,213 @@ private:
std::vector<u8> byte_array;
};
-class H264 {
-public:
- explicit H264(Host1x::Host1x& host1x);
- ~H264();
-
- /// Compose the H264 frame for FFmpeg decoding
- [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
- size_t* out_configuration_size,
- bool is_first_frame = false);
+struct Offset {
+ constexpr u32 Address() const noexcept {
+ return offset << 8;
+ }
private:
- Common::ScratchBuffer<u8> frame;
- Common::ScratchBuffer<u8> scan;
- Host1x::Host1x& host1x;
-
- struct H264ParameterSet {
- s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
- s32 delta_pic_order_always_zero_flag; ///< 0x04
- s32 frame_mbs_only_flag; ///< 0x08
- u32 pic_width_in_mbs; ///< 0x0C
- u32 frame_height_in_map_units; ///< 0x10
- union { ///< 0x14
- BitField<0, 2, u32> tile_format;
- BitField<2, 3, u32> gob_height;
- };
- u32 entropy_coding_mode_flag; ///< 0x18
- s32 pic_order_present_flag; ///< 0x1C
- s32 num_refidx_l0_default_active; ///< 0x20
- s32 num_refidx_l1_default_active; ///< 0x24
- s32 deblocking_filter_control_present_flag; ///< 0x28
- s32 redundant_pic_cnt_present_flag; ///< 0x2C
- u32 transform_8x8_mode_flag; ///< 0x30
- u32 pitch_luma; ///< 0x34
- u32 pitch_chroma; ///< 0x38
- u32 luma_top_offset; ///< 0x3C
- u32 luma_bot_offset; ///< 0x40
- u32 luma_frame_offset; ///< 0x44
- u32 chroma_top_offset; ///< 0x48
- u32 chroma_bot_offset; ///< 0x4C
- u32 chroma_frame_offset; ///< 0x50
- u32 hist_buffer_size; ///< 0x54
- union { ///< 0x58
- union {
- BitField<0, 1, u64> mbaff_frame;
- BitField<1, 1, u64> direct_8x8_inference;
- BitField<2, 1, u64> weighted_pred;
- BitField<3, 1, u64> constrained_intra_pred;
- BitField<4, 1, u64> ref_pic;
- BitField<5, 1, u64> field_pic;
- BitField<6, 1, u64> bottom_field;
- BitField<7, 1, u64> second_field;
- } flags;
- BitField<8, 4, u64> log2_max_frame_num_minus4;
- BitField<12, 2, u64> chroma_format_idc;
- BitField<14, 2, u64> pic_order_cnt_type;
- BitField<16, 6, s64> pic_init_qp_minus26;
- BitField<22, 5, s64> chroma_qp_index_offset;
- BitField<27, 5, s64> second_chroma_qp_index_offset;
- BitField<32, 2, u64> weighted_bipred_idc;
- BitField<34, 7, u64> curr_pic_idx;
- BitField<41, 5, u64> curr_col_idx;
- BitField<46, 16, u64> frame_number;
- BitField<62, 1, u64> frame_surfaces;
- BitField<63, 1, u64> output_memory_layout;
- };
+ u32 offset;
+};
+static_assert(std::is_trivial_v<Offset>, "Offset must be trivial");
+static_assert(sizeof(Offset) == 0x4, "Offset has the wrong size!");
+
+struct H264ParameterSet {
+ s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
+ s32 delta_pic_order_always_zero_flag; ///< 0x04
+ s32 frame_mbs_only_flag; ///< 0x08
+ u32 pic_width_in_mbs; ///< 0x0C
+ u32 frame_height_in_mbs; ///< 0x10
+ union { ///< 0x14
+ BitField<0, 2, u32> tile_format;
+ BitField<2, 3, u32> gob_height;
+ BitField<5, 27, u32> reserved_surface_format;
};
- static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size");
-
- struct H264DecoderContext {
- INSERT_PADDING_WORDS_NOINIT(18); ///< 0x0000
- u32 stream_len; ///< 0x0048
- INSERT_PADDING_WORDS_NOINIT(3); ///< 0x004C
- H264ParameterSet h264_parameter_set; ///< 0x0058
- INSERT_PADDING_WORDS_NOINIT(66); ///< 0x00B8
- std::array<u8, 0x60> weight_scale; ///< 0x01C0
- std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220
+ u32 entropy_coding_mode_flag; ///< 0x18
+ s32 pic_order_present_flag; ///< 0x1C
+ s32 num_refidx_l0_default_active; ///< 0x20
+ s32 num_refidx_l1_default_active; ///< 0x24
+ s32 deblocking_filter_control_present_flag; ///< 0x28
+ s32 redundant_pic_cnt_present_flag; ///< 0x2C
+ u32 transform_8x8_mode_flag; ///< 0x30
+ u32 pitch_luma; ///< 0x34
+ u32 pitch_chroma; ///< 0x38
+ Offset luma_top_offset; ///< 0x3C
+ Offset luma_bot_offset; ///< 0x40
+ Offset luma_frame_offset; ///< 0x44
+ Offset chroma_top_offset; ///< 0x48
+ Offset chroma_bot_offset; ///< 0x4C
+ Offset chroma_frame_offset; ///< 0x50
+ u32 hist_buffer_size; ///< 0x54
+ union { ///< 0x58
+ union {
+ BitField<0, 1, u64> mbaff_frame;
+ BitField<1, 1, u64> direct_8x8_inference;
+ BitField<2, 1, u64> weighted_pred;
+ BitField<3, 1, u64> constrained_intra_pred;
+ BitField<4, 1, u64> ref_pic;
+ BitField<5, 1, u64> field_pic;
+ BitField<6, 1, u64> bottom_field;
+ BitField<7, 1, u64> second_field;
+ } flags;
+ BitField<8, 4, u64> log2_max_frame_num_minus4;
+ BitField<12, 2, u64> chroma_format_idc;
+ BitField<14, 2, u64> pic_order_cnt_type;
+ BitField<16, 6, s64> pic_init_qp_minus26;
+ BitField<22, 5, s64> chroma_qp_index_offset;
+ BitField<27, 5, s64> second_chroma_qp_index_offset;
+ BitField<32, 2, u64> weighted_bipred_idc;
+ BitField<34, 7, u64> curr_pic_idx;
+ BitField<41, 5, u64> curr_col_idx;
+ BitField<46, 16, u64> frame_number;
+ BitField<62, 1, u64> frame_surfaces;
+ BitField<63, 1, u64> output_memory_layout;
};
- static_assert(sizeof(H264DecoderContext) == 0x2A0, "H264DecoderContext is an invalid size");
+};
+static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size");
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264ParameterSet, field_name) == position, \
"Field " #field_name " has invalid position")
- ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00);
- ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04);
- ASSERT_POSITION(frame_mbs_only_flag, 0x08);
- ASSERT_POSITION(pic_width_in_mbs, 0x0C);
- ASSERT_POSITION(frame_height_in_map_units, 0x10);
- ASSERT_POSITION(tile_format, 0x14);
- ASSERT_POSITION(entropy_coding_mode_flag, 0x18);
- ASSERT_POSITION(pic_order_present_flag, 0x1C);
- ASSERT_POSITION(num_refidx_l0_default_active, 0x20);
- ASSERT_POSITION(num_refidx_l1_default_active, 0x24);
- ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28);
- ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C);
- ASSERT_POSITION(transform_8x8_mode_flag, 0x30);
- ASSERT_POSITION(pitch_luma, 0x34);
- ASSERT_POSITION(pitch_chroma, 0x38);
- ASSERT_POSITION(luma_top_offset, 0x3C);
- ASSERT_POSITION(luma_bot_offset, 0x40);
- ASSERT_POSITION(luma_frame_offset, 0x44);
- ASSERT_POSITION(chroma_top_offset, 0x48);
- ASSERT_POSITION(chroma_bot_offset, 0x4C);
- ASSERT_POSITION(chroma_frame_offset, 0x50);
- ASSERT_POSITION(hist_buffer_size, 0x54);
- ASSERT_POSITION(flags, 0x58);
+ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00);
+ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04);
+ASSERT_POSITION(frame_mbs_only_flag, 0x08);
+ASSERT_POSITION(pic_width_in_mbs, 0x0C);
+ASSERT_POSITION(frame_height_in_mbs, 0x10);
+ASSERT_POSITION(tile_format, 0x14);
+ASSERT_POSITION(entropy_coding_mode_flag, 0x18);
+ASSERT_POSITION(pic_order_present_flag, 0x1C);
+ASSERT_POSITION(num_refidx_l0_default_active, 0x20);
+ASSERT_POSITION(num_refidx_l1_default_active, 0x24);
+ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28);
+ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C);
+ASSERT_POSITION(transform_8x8_mode_flag, 0x30);
+ASSERT_POSITION(pitch_luma, 0x34);
+ASSERT_POSITION(pitch_chroma, 0x38);
+ASSERT_POSITION(luma_top_offset, 0x3C);
+ASSERT_POSITION(luma_bot_offset, 0x40);
+ASSERT_POSITION(luma_frame_offset, 0x44);
+ASSERT_POSITION(chroma_top_offset, 0x48);
+ASSERT_POSITION(chroma_bot_offset, 0x4C);
+ASSERT_POSITION(chroma_frame_offset, 0x50);
+ASSERT_POSITION(hist_buffer_size, 0x54);
+ASSERT_POSITION(flags, 0x58);
#undef ASSERT_POSITION
+struct DpbEntry {
+ union {
+ BitField<0, 7, u32> index;
+ BitField<7, 5, u32> col_idx;
+ BitField<12, 2, u32> state;
+ BitField<14, 1, u32> is_long_term;
+ BitField<15, 1, u32> non_existing;
+ BitField<16, 1, u32> is_field;
+ BitField<17, 4, u32> top_field_marking;
+ BitField<21, 4, u32> bottom_field_marking;
+ BitField<25, 1, u32> output_memory_layout;
+ BitField<26, 6, u32> reserved;
+ } flags;
+ std::array<u32, 2> field_order_cnt;
+ u32 frame_idx;
+};
+static_assert(sizeof(DpbEntry) == 0x10, "DpbEntry has the wrong size!");
+
+struct DisplayParam {
+ union {
+ BitField<0, 1, u32> enable_tf_output;
+ BitField<1, 1, u32> vc1_map_y_flag;
+ BitField<2, 3, u32> map_y_value;
+ BitField<5, 1, u32> vc1_map_uv_flag;
+ BitField<6, 3, u32> map_uv_value;
+ BitField<9, 8, u32> out_stride;
+ BitField<17, 3, u32> tiling_format;
+ BitField<20, 1, u32> output_structure; // 0=frame, 1=field
+ BitField<21, 11, u32> reserved0;
+ };
+ std::array<s32, 2> output_top;
+ std::array<s32, 2> output_bottom;
+ union {
+ BitField<0, 1, u32> enable_histogram;
+ BitField<1, 12, u32> histogram_start_x;
+ BitField<13, 12, u32> histogram_start_y;
+ BitField<25, 7, u32> reserved1;
+ };
+ union {
+ BitField<0, 12, u32> histogram_end_x;
+ BitField<12, 12, u32> histogram_end_y;
+ BitField<24, 8, u32> reserved2;
+ };
+};
+static_assert(sizeof(DisplayParam) == 0x1C, "DisplayParam has the wrong size!");
+
+struct H264DecoderContext {
+ INSERT_PADDING_WORDS_NOINIT(13); ///< 0x0000
+ std::array<u8, 16> eos; ///< 0x0034
+ u8 explicit_eos_present_flag; ///< 0x0044
+ u8 hint_dump_en; ///< 0x0045
+ INSERT_PADDING_BYTES_NOINIT(2); ///< 0x0046
+ u32 stream_len; ///< 0x0048
+ u32 slice_count; ///< 0x004C
+ u32 mbhist_buffer_size; ///< 0x0050
+ u32 gptimer_timeout_value; ///< 0x0054
+ H264ParameterSet h264_parameter_set; ///< 0x0058
+ std::array<s32, 2> curr_field_order_cnt; ///< 0x00B8
+ std::array<DpbEntry, 16> dpb; ///< 0x00C0
+ std::array<u8, 0x60> weight_scale_4x4; ///< 0x01C0
+ std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220
+ std::array<u8, 2> num_inter_view_refs_lX; ///< 0x02A0
+ std::array<u8, 14> reserved2; ///< 0x02A2
+ std::array<std::array<s8, 16>, 2> inter_view_refidx_lX; ///< 0x02B0
+ union { ///< 0x02D0
+ BitField<0, 1, u32> lossless_ipred8x8_filter_enable;
+ BitField<1, 1, u32> qpprime_y_zero_transform_bypass_flag;
+ BitField<2, 30, u32> reserved3;
+ };
+ DisplayParam display_param; ///< 0x02D4
+ std::array<u32, 3> reserved4; ///< 0x02F0
+};
+static_assert(sizeof(H264DecoderContext) == 0x2FC, "H264DecoderContext is an invalid size");
+
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264DecoderContext, field_name) == position, \
"Field " #field_name " has invalid position")
- ASSERT_POSITION(stream_len, 0x48);
- ASSERT_POSITION(h264_parameter_set, 0x58);
- ASSERT_POSITION(weight_scale, 0x1C0);
+ASSERT_POSITION(stream_len, 0x48);
+ASSERT_POSITION(h264_parameter_set, 0x58);
+ASSERT_POSITION(dpb, 0xC0);
+ASSERT_POSITION(weight_scale_4x4, 0x1C0);
#undef ASSERT_POSITION
+
+class H264 final : public Decoder {
+public:
+ explicit H264(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
+ Host1x::FrameQueue& frame_queue);
+ ~H264() override;
+
+ H264(const H264&) = delete;
+ H264& operator=(const H264&) = delete;
+
+ H264(H264&&) = delete;
+ H264& operator=(H264&&) = delete;
+
+ /// Compose the H264 frame for FFmpeg decoding
+ [[nodiscard]] std::span<const u8> ComposeFrame() override;
+
+ std::tuple<u64, u64> GetProgressiveOffsets() override;
+ std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override;
+ bool IsInterlaced() override;
+
+ std::string_view GetCurrentCodecName() const override {
+ return "H264";
+ }
+
+private:
+ bool is_first_frame{true};
+ Common::ScratchBuffer<u8> frame_scratch;
+ Common::ScratchBuffer<u8> scan_scratch;
+ H264DecoderContext current_context{};
};
-} // namespace Decoder
+} // namespace Decoders
} // namespace Tegra
diff --git a/src/video_core/host1x/codecs/vp8.cpp b/src/video_core/host1x/codecs/vp8.cpp
index be97e3b006..6094f16e0e 100644
--- a/src/video_core/host1x/codecs/vp8.cpp
+++ b/src/video_core/host1x/codecs/vp8.cpp
@@ -7,47 +7,70 @@
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
-namespace Tegra::Decoder {
-VP8::VP8(Host1x::Host1x& host1x_) : host1x{host1x_} {}
+namespace Tegra::Decoders {
+VP8::VP8(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_,
+ Host1x::FrameQueue& frame_queue_)
+ : Decoder{host1x_, id_, regs_, frame_queue_} {
+ codec = Host1x::NvdecCommon::VideoCodec::VP8;
+ initialized = decode_api.Initialize(codec);
+}
VP8::~VP8() = default;
-std::span<const u8> VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
- VP8PictureInfo info;
- host1x.GMMU().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo));
+std::tuple<u64, u64> VP8::GetProgressiveOffsets() {
+ auto luma{regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
+ auto chroma{regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
+ return {luma, chroma};
+}
+
+std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() {
+ auto luma_top{regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
+ auto luma_bottom{
+ regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
+ auto chroma_top{
+ regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
+ auto chroma_bottom{
+ regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
+ return {luma_top, luma_bottom, chroma_top, chroma_bottom};
+}
+
+std::span<const u8> VP8::ComposeFrame() {
+ memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context,
+ sizeof(VP8PictureInfo));
- const bool is_key_frame = info.key_frame == 1u;
- const auto bitstream_size = static_cast<size_t>(info.vld_buffer_size);
+ const bool is_key_frame = current_context.key_frame == 1u;
+ const auto bitstream_size = static_cast<size_t>(current_context.vld_buffer_size);
const size_t header_size = is_key_frame ? 10u : 3u;
- frame.resize(header_size + bitstream_size);
+ frame_scratch.resize(header_size + bitstream_size);
// Based on page 30 of the VP8 specification.
// https://datatracker.ietf.org/doc/rfc6386/
- frame[0] = is_key_frame ? 0u : 1u; // 1-bit frame type (0: keyframe, 1: interframes).
- frame[0] |= static_cast<u8>((info.version & 7u) << 1u); // 3-bit version number
- frame[0] |= static_cast<u8>(1u << 4u); // 1-bit show_frame flag
+ frame_scratch[0] = is_key_frame ? 0u : 1u; // 1-bit frame type (0: keyframe, 1: interframes).
+ frame_scratch[0] |=
+ static_cast<u8>((current_context.version & 7u) << 1u); // 3-bit version number
+ frame_scratch[0] |= static_cast<u8>(1u << 4u); // 1-bit show_frame flag
// The next 19-bits are the first partition size
- frame[0] |= static_cast<u8>((info.first_part_size & 7u) << 5u);
- frame[1] = static_cast<u8>((info.first_part_size & 0x7f8u) >> 3u);
- frame[2] = static_cast<u8>((info.first_part_size & 0x7f800u) >> 11u);
+ frame_scratch[0] |= static_cast<u8>((current_context.first_part_size & 7u) << 5u);
+ frame_scratch[1] = static_cast<u8>((current_context.first_part_size & 0x7f8u) >> 3u);
+ frame_scratch[2] = static_cast<u8>((current_context.first_part_size & 0x7f800u) >> 11u);
if (is_key_frame) {
- frame[3] = 0x9du;
- frame[4] = 0x01u;
- frame[5] = 0x2au;
+ frame_scratch[3] = 0x9du;
+ frame_scratch[4] = 0x01u;
+ frame_scratch[5] = 0x2au;
// TODO(ameerj): Horizontal/Vertical Scale
// 16 bits: (2 bits Horizontal Scale << 14) | Width (14 bits)
- frame[6] = static_cast<u8>(info.frame_width & 0xff);
- frame[7] = static_cast<u8>(((info.frame_width >> 8) & 0x3f));
+ frame_scratch[6] = static_cast<u8>(current_context.frame_width & 0xff);
+ frame_scratch[7] = static_cast<u8>(((current_context.frame_width >> 8) & 0x3f));
// 16 bits:(2 bits Vertical Scale << 14) | Height (14 bits)
- frame[8] = static_cast<u8>(info.frame_height & 0xff);
- frame[9] = static_cast<u8>(((info.frame_height >> 8) & 0x3f));
+ frame_scratch[8] = static_cast<u8>(current_context.frame_height & 0xff);
+ frame_scratch[9] = static_cast<u8>(((current_context.frame_height >> 8) & 0x3f));
}
- const u64 bitstream_offset = state.frame_bitstream_offset;
- host1x.GMMU().ReadBlock(bitstream_offset, frame.data() + header_size, bitstream_size);
+ const u64 bitstream_offset = regs.frame_bitstream_offset.Address();
+ memory_manager.ReadBlock(bitstream_offset, frame_scratch.data() + header_size, bitstream_size);
- return frame;
+ return frame_scratch;
}
-} // namespace Tegra::Decoder
+} // namespace Tegra::Decoders
diff --git a/src/video_core/host1x/codecs/vp8.h b/src/video_core/host1x/codecs/vp8.h
index 5945e46586..74800281d8 100644
--- a/src/video_core/host1x/codecs/vp8.h
+++ b/src/video_core/host1x/codecs/vp8.h
@@ -9,6 +9,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/scratch_buffer.h"
+#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/nvdec_common.h"
namespace Tegra {
@@ -17,20 +18,41 @@ namespace Host1x {
class Host1x;
} // namespace Host1x
-namespace Decoder {
+namespace Decoders {
+enum class Vp8SurfaceIndex : u32 {
+ Last = 0,
+ Golden = 1,
+ AltRef = 2,
+ Current = 3,
+};
-class VP8 {
+class VP8 final : public Decoder {
public:
- explicit VP8(Host1x::Host1x& host1x);
- ~VP8();
+ explicit VP8(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
+ Host1x::FrameQueue& frame_queue);
+ ~VP8() override;
+
+ VP8(const VP8&) = delete;
+ VP8& operator=(const VP8&) = delete;
+
+ VP8(VP8&&) = delete;
+ VP8& operator=(VP8&&) = delete;
+
+ [[nodiscard]] std::span<const u8> ComposeFrame() override;
- /// Compose the VP8 frame for FFmpeg decoding
- [[nodiscard]] std::span<const u8> ComposeFrame(
- const Host1x::NvdecCommon::NvdecRegisters& state);
+ std::tuple<u64, u64> GetProgressiveOffsets() override;
+ std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override;
+
+ bool IsInterlaced() override {
+ return false;
+ }
+
+ std::string_view GetCurrentCodecName() const override {
+ return "VP8";
+ }
private:
- Common::ScratchBuffer<u8> frame;
- Host1x::Host1x& host1x;
+ Common::ScratchBuffer<u8> frame_scratch;
struct VP8PictureInfo {
INSERT_PADDING_WORDS_NOINIT(14);
@@ -73,7 +95,9 @@ private:
INSERT_PADDING_WORDS_NOINIT(3);
};
static_assert(sizeof(VP8PictureInfo) == 0xc0, "PictureInfo is an invalid size");
+
+ VP8PictureInfo current_context{};
};
-} // namespace Decoder
+} // namespace Decoders
} // namespace Tegra
diff --git a/src/video_core/host1x/codecs/vp9.cpp b/src/video_core/host1x/codecs/vp9.cpp
index 65d6fb2d57..c70d0a506f 100644
--- a/src/video_core/host1x/codecs/vp9.cpp
+++ b/src/video_core/host1x/codecs/vp9.cpp
@@ -4,12 +4,13 @@
#include <algorithm> // for std::copy
#include <numeric>
+#include "common/alignment.h"
#include "common/assert.h"
#include "video_core/host1x/codecs/vp9.h"
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
-namespace Tegra::Decoder {
+namespace Tegra::Decoders {
namespace {
constexpr u32 diff_update_probability = 252;
constexpr u32 frame_sync_code = 0x498342;
@@ -237,7 +238,12 @@ constexpr std::array<u8, 254> map_lut{
}
} // Anonymous namespace
-VP9::VP9(Host1x::Host1x& host1x_) : host1x{host1x_} {}
+VP9::VP9(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_,
+ Host1x::FrameQueue& frame_queue_)
+ : Decoder{host1x_, id_, regs_, frame_queue_} {
+ codec = Host1x::NvdecCommon::VideoCodec::VP9;
+ initialized = decode_api.Initialize(codec);
+}
VP9::~VP9() = default;
@@ -356,35 +362,113 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_
}
}
-Vp9PictureInfo VP9::GetVp9PictureInfo(const Host1x::NvdecCommon::NvdecRegisters& state) {
- PictureInfo picture_info;
- host1x.GMMU().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo));
- Vp9PictureInfo vp9_info = picture_info.Convert();
+void VP9::WriteSegmentation(VpxBitStreamWriter& writer) {
+ bool enabled = current_picture_info.segmentation.enabled != 0;
+ writer.WriteBit(enabled);
+ if (!enabled) {
+ return;
+ }
+
+ auto update_map = current_picture_info.segmentation.update_map != 0;
+ writer.WriteBit(update_map);
+
+ if (update_map) {
+ EntropyProbs entropy_probs{};
+ memory_manager.ReadBlock(regs.vp9_prob_tab_buffer_offset.Address(), &entropy_probs,
+ sizeof(entropy_probs));
+
+ auto WriteProb = [&](u8 prob) {
+ bool coded = prob != 255;
+ writer.WriteBit(coded);
+ if (coded) {
+ writer.WriteU(prob, 8);
+ }
+ };
+
+ for (size_t i = 0; i < entropy_probs.mb_segment_tree_probs.size(); i++) {
+ WriteProb(entropy_probs.mb_segment_tree_probs[i]);
+ }
+
+ auto temporal_update = current_picture_info.segmentation.temporal_update != 0;
+ writer.WriteBit(temporal_update);
+
+ if (temporal_update) {
+ for (s32 i = 0; i < 3; i++) {
+ WriteProb(entropy_probs.segment_pred_probs[i]);
+ }
+ }
+ }
+
+ if (last_segmentation == current_picture_info.segmentation) {
+ writer.WriteBit(false);
+ return;
+ }
+
+ last_segmentation = current_picture_info.segmentation;
+ writer.WriteBit(true);
+ writer.WriteBit(current_picture_info.segmentation.abs_delta != 0);
+
+ constexpr s32 MAX_SEGMENTS = 8;
+ constexpr std::array SegmentationFeatureBits = {8, 6, 2, 0};
+
+ for (s32 i = 0; i < MAX_SEGMENTS; i++) {
+ auto q_enabled = current_picture_info.segmentation.feature_enabled[i][0] != 0;
+ writer.WriteBit(q_enabled);
+ if (q_enabled) {
+ writer.WriteS(current_picture_info.segmentation.feature_data[i][0],
+ SegmentationFeatureBits[0]);
+ }
+
+ auto lf_enabled = current_picture_info.segmentation.feature_enabled[i][1] != 0;
+ writer.WriteBit(lf_enabled);
+ if (lf_enabled) {
+ writer.WriteS(current_picture_info.segmentation.feature_data[i][1],
+ SegmentationFeatureBits[1]);
+ }
+
+ auto ref_enabled = current_picture_info.segmentation.feature_enabled[i][2] != 0;
+ writer.WriteBit(ref_enabled);
+ if (ref_enabled) {
+ writer.WriteU(current_picture_info.segmentation.feature_data[i][2],
+ SegmentationFeatureBits[2]);
+ }
+
+ auto skip_enabled = current_picture_info.segmentation.feature_enabled[i][3] != 0;
+ writer.WriteBit(skip_enabled);
+ }
+}
+
+Vp9PictureInfo VP9::GetVp9PictureInfo() {
+ memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_picture_info,
+ sizeof(PictureInfo));
+ Vp9PictureInfo vp9_info = current_picture_info.Convert();
- InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy);
+ InsertEntropy(regs.vp9_prob_tab_buffer_offset.Address(), vp9_info.entropy);
// surface_luma_offset[0:3] contains the address of the reference frame offsets in the following
// order: last, golden, altref, current.
- std::copy(state.surface_luma_offset.begin(), state.surface_luma_offset.begin() + 4,
- vp9_info.frame_offsets.begin());
+ for (size_t i = 0; i < 4; i++) {
+ vp9_info.frame_offsets[i] = regs.surface_luma_offsets[i].Address();
+ }
return vp9_info;
}
void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
EntropyProbs entropy;
- host1x.GMMU().ReadBlock(offset, &entropy, sizeof(EntropyProbs));
+ memory_manager.ReadBlock(offset, &entropy, sizeof(EntropyProbs));
entropy.Convert(dst);
}
-Vp9FrameContainer VP9::GetCurrentFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
+Vp9FrameContainer VP9::GetCurrentFrame() {
Vp9FrameContainer current_frame{};
{
// gpu.SyncGuestHost(); epic, why?
- current_frame.info = GetVp9PictureInfo(state);
+ current_frame.info = GetVp9PictureInfo();
current_frame.bit_stream.resize(current_frame.info.bitstream_size);
- host1x.GMMU().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(),
- current_frame.info.bitstream_size);
+ memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(),
+ current_frame.bit_stream.data(),
+ current_frame.info.bitstream_size);
}
if (!next_frame.bit_stream.empty()) {
Vp9FrameContainer temp{
@@ -742,8 +826,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q);
uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q);
- ASSERT(!current_frame_info.segment_enabled);
- uncomp_writer.WriteBit(false); // Segmentation enabled (TODO).
+ WriteSegmentation(uncomp_writer);
const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width);
const s32 max_tile_cols_log2 = CalcMaxLog2TileCols(current_frame_info.frame_size.width);
@@ -770,10 +853,29 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
return uncomp_writer;
}
-void VP9::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
+std::tuple<u64, u64> VP9::GetProgressiveOffsets() {
+ auto luma{regs.surface_luma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
+ auto chroma{regs.surface_chroma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
+ return {luma, chroma};
+}
+
+std::tuple<u64, u64, u64, u64> VP9::GetInterlacedOffsets() {
+ auto luma_top{regs.surface_luma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
+ auto luma_bottom{
+ regs.surface_luma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
+ auto chroma_top{
+ regs.surface_chroma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
+ auto chroma_bottom{
+ regs.surface_chroma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
+ return {luma_top, luma_bottom, chroma_top, chroma_bottom};
+}
+
+std::span<const u8> VP9::ComposeFrame() {
+ vp9_hidden_frame = false;
+
std::vector<u8> bitstream;
{
- Vp9FrameContainer curr_frame = GetCurrentFrame(state);
+ Vp9FrameContainer curr_frame = GetCurrentFrame();
current_frame_info = curr_frame.info;
bitstream = std::move(curr_frame.bit_stream);
}
@@ -786,12 +888,16 @@ void VP9::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
std::vector<u8> uncompressed_header = uncomp_writer.GetByteArray();
// Write headers and frame to buffer
- frame.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size());
- std::copy(uncompressed_header.begin(), uncompressed_header.end(), frame.begin());
+ frame_scratch.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size());
+ std::copy(uncompressed_header.begin(), uncompressed_header.end(), frame_scratch.begin());
std::copy(compressed_header.begin(), compressed_header.end(),
- frame.begin() + uncompressed_header.size());
+ frame_scratch.begin() + uncompressed_header.size());
std::copy(bitstream.begin(), bitstream.end(),
- frame.begin() + uncompressed_header.size() + compressed_header.size());
+ frame_scratch.begin() + uncompressed_header.size() + compressed_header.size());
+
+ vp9_hidden_frame = WasFrameHidden();
+
+ return GetFrameBytes();
}
VpxRangeEncoder::VpxRangeEncoder() {
@@ -944,4 +1050,4 @@ const std::vector<u8>& VpxBitStreamWriter::GetByteArray() const {
return byte_array;
}
-} // namespace Tegra::Decoder
+} // namespace Tegra::Decoders
diff --git a/src/video_core/host1x/codecs/vp9.h b/src/video_core/host1x/codecs/vp9.h
index f1ed195081..9d42033cb3 100644
--- a/src/video_core/host1x/codecs/vp9.h
+++ b/src/video_core/host1x/codecs/vp9.h
@@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "common/scratch_buffer.h"
#include "common/stream.h"
+#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/codecs/vp9_types.h"
#include "video_core/host1x/nvdec_common.h"
@@ -19,7 +20,7 @@ namespace Host1x {
class Host1x;
} // namespace Host1x
-namespace Decoder {
+namespace Decoders {
/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the
/// VP9 header bitstreams.
@@ -110,21 +111,32 @@ private:
std::vector<u8> byte_array;
};
-class VP9 {
+class VP9 final : public Decoder {
public:
- explicit VP9(Host1x::Host1x& host1x);
- ~VP9();
+ explicit VP9(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
+ Host1x::FrameQueue& frame_queue);
+ ~VP9() override;
VP9(const VP9&) = delete;
VP9& operator=(const VP9&) = delete;
- VP9(VP9&&) = default;
+ VP9(VP9&&) = delete;
VP9& operator=(VP9&&) = delete;
- /// Composes the VP9 frame from the GPU state information.
- /// Based on the official VP9 spec documentation
- void ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state);
+ [[nodiscard]] std::span<const u8> ComposeFrame() override;
+ std::tuple<u64, u64> GetProgressiveOffsets() override;
+ std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override;
+
+ bool IsInterlaced() override {
+ return false;
+ }
+
+ std::string_view GetCurrentCodecName() const override {
+ return "VP9";
+ }
+
+private:
/// Returns true if the most recent frame was a hidden frame.
[[nodiscard]] bool WasFrameHidden() const {
return !current_frame_info.show_frame;
@@ -132,10 +144,9 @@ public:
/// Returns a const span to the composed frame data.
[[nodiscard]] std::span<const u8> GetFrameBytes() const {
- return frame;
+ return frame_scratch;
}
-private:
/// Generates compressed header probability updates in the bitstream writer
template <typename T, std::size_t N>
void WriteProbabilityUpdate(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
@@ -167,23 +178,22 @@ private:
/// Write motion vector probability updates. 6.3.17 in the spec
void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
+ void WriteSegmentation(VpxBitStreamWriter& writer);
+
/// Returns VP9 information from NVDEC provided offset and size
- [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo(
- const Host1x::NvdecCommon::NvdecRegisters& state);
+ [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo();
/// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct
void InsertEntropy(u64 offset, Vp9EntropyProbs& dst);
/// Returns frame to be decoded after buffering
- [[nodiscard]] Vp9FrameContainer GetCurrentFrame(
- const Host1x::NvdecCommon::NvdecRegisters& state);
+ [[nodiscard]] Vp9FrameContainer GetCurrentFrame();
/// Use NVDEC providied information to compose the headers for the current frame
[[nodiscard]] std::vector<u8> ComposeCompressedHeader();
[[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader();
- Host1x::Host1x& host1x;
- Common::ScratchBuffer<u8> frame;
+ Common::ScratchBuffer<u8> frame_scratch;
std::array<s8, 4> loop_filter_ref_deltas{};
std::array<s8, 2> loop_filter_mode_deltas{};
@@ -192,9 +202,11 @@ private:
std::array<Vp9EntropyProbs, 4> frame_ctxs{};
bool swap_ref_indices{};
+ Segmentation last_segmentation{};
+ PictureInfo current_picture_info{};
Vp9PictureInfo current_frame_info{};
Vp9EntropyProbs prev_frame_probs{};
};
-} // namespace Decoder
+} // namespace Decoders
} // namespace Tegra
diff --git a/src/video_core/host1x/codecs/vp9_types.h b/src/video_core/host1x/codecs/vp9_types.h
index cc9b256907..77535d5f66 100644
--- a/src/video_core/host1x/codecs/vp9_types.h
+++ b/src/video_core/host1x/codecs/vp9_types.h
@@ -11,7 +11,14 @@
namespace Tegra {
-namespace Decoder {
+namespace Decoders {
+enum class Vp9SurfaceIndex : u32 {
+ Last = 0,
+ Golden = 1,
+ AltRef = 2,
+ Current = 3,
+};
+
struct Vp9FrameDimensions {
s16 width;
s16 height;
@@ -48,11 +55,13 @@ enum class TxMode {
};
struct Segmentation {
+ constexpr bool operator==(const Segmentation& rhs) const = default;
+
u8 enabled;
u8 update_map;
u8 temporal_update;
u8 abs_delta;
- std::array<u32, 8> feature_mask;
+ std::array<std::array<u8, 4>, 8> feature_enabled;
std::array<std::array<s16, 4>, 8> feature_data;
};
static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size");
@@ -190,7 +199,17 @@ struct PictureInfo {
static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size");
struct EntropyProbs {
- INSERT_PADDING_BYTES_NOINIT(1024); ///< 0x0000
+ std::array<u8, 10 * 10 * 8> kf_bmode_prob; ///< 0x0000
+ std::array<u8, 10 * 10 * 1> kf_bmode_probB; ///< 0x0320
+ std::array<u8, 3> ref_pred_probs; ///< 0x0384
+ std::array<u8, 7> mb_segment_tree_probs; ///< 0x0387
+ std::array<u8, 3> segment_pred_probs; ///< 0x038E
+ std::array<u8, 4> ref_scores; ///< 0x0391
+ std::array<u8, 2> prob_comppred; ///< 0x0395
+ INSERT_PADDING_BYTES_NOINIT(9); ///< 0x0397
+ std::array<u8, 10 * 8> kf_uv_mode_prob; ///< 0x03A0
+ std::array<u8, 10 * 1> kf_uv_mode_probB; ///< 0x03F0
+ INSERT_PADDING_BYTES_NOINIT(6); ///< 0x03FA
std::array<u8, 28> inter_mode_prob; ///< 0x0400
std::array<u8, 4> intra_inter_prob; ///< 0x041C
INSERT_PADDING_BYTES_NOINIT(80); ///< 0x0420
@@ -302,5 +321,5 @@ ASSERT_POSITION(class_0_fr, 0x560);
ASSERT_POSITION(coef_probs, 0x5A0);
#undef ASSERT_POSITION
-}; // namespace Decoder
+}; // namespace Decoders
}; // namespace Tegra