Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6cab5068b | ||
|
|
b5512f0708 |
24 changed files with 489 additions and 564 deletions
|
|
@ -89,7 +89,7 @@ void NvMap::UnmapHandle(Handle& handle_description) {
|
|||
|
||||
// Free and unmap the handle from Host1x GMMU
|
||||
if (handle_description.pin_virt_address) {
|
||||
host1x.GMMU().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
|
||||
host1x.gmmu_manager.Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
|
||||
handle_description.aligned_size);
|
||||
host1x.Allocator().Free(handle_description.pin_virt_address,
|
||||
static_cast<u32>(handle_description.aligned_size));
|
||||
|
|
@ -169,12 +169,8 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
|
|||
std::scoped_lock lock(handle_description->mutex);
|
||||
const auto map_low_area = [&] {
|
||||
if (handle_description->pin_virt_address == 0) {
|
||||
auto& gmmu_allocator = host1x.Allocator();
|
||||
auto& gmmu = host1x.GMMU();
|
||||
u32 address =
|
||||
gmmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size));
|
||||
gmmu.Map(static_cast<GPUVAddr>(address), handle_description->d_address,
|
||||
handle_description->aligned_size);
|
||||
u32 address = host1x.Allocator().Allocate(u32(handle_description->aligned_size));
|
||||
host1x.gmmu_manager.Map(GPUVAddr(address), handle_description->d_address, handle_description->aligned_size);
|
||||
handle_description->pin_virt_address = address;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ add_subdirectory(host_shaders)
|
|||
add_subdirectory(gpu_logging)
|
||||
|
||||
if(LIBVA_FOUND)
|
||||
set_source_files_properties(host1x/ffmpeg/ffmpeg.cpp
|
||||
set_source_files_properties(host1x/ffmpeg.cpp
|
||||
PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
|
||||
list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
|
||||
endif()
|
||||
|
|
@ -72,9 +72,9 @@ add_library(video_core STATIC
|
|||
host1x/codecs/vp8.h
|
||||
host1x/codecs/vp9.cpp
|
||||
host1x/codecs/vp9.h
|
||||
host1x/codecs/vp9_types.h
|
||||
host1x/ffmpeg/ffmpeg.cpp
|
||||
host1x/ffmpeg/ffmpeg.h
|
||||
host1x/codec_types.h
|
||||
host1x/ffmpeg.cpp
|
||||
host1x/ffmpeg.h
|
||||
host1x/control.cpp
|
||||
host1x/control.h
|
||||
host1x/gpu_device_memory_manager.cpp
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Ryujinx Team and Contributors
|
||||
|
|
@ -20,9 +20,10 @@
|
|||
namespace Tegra {
|
||||
|
||||
CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id)
|
||||
: host1x{host1x_}, memory_manager{host1x.GMMU()},
|
||||
host_processor{std::make_unique<Host1x::Control>(host1x_)}, current_class{
|
||||
static_cast<ChClassId>(id)} {
|
||||
: host_processor{std::make_unique<Host1x::Control>(host1x_)}
|
||||
, host1x{host1x_}
|
||||
, current_class{ChClassId(id)}
|
||||
{
|
||||
thread = std::jthread([this](std::stop_token stop_token) { ProcessEntries(stop_token); });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -117,9 +120,6 @@ protected:
|
|||
|
||||
virtual void ProcessMethod(u32 method, u32 arg) = 0;
|
||||
|
||||
Host1x::Host1x& host1x;
|
||||
Tegra::MemoryManager& memory_manager;
|
||||
|
||||
private:
|
||||
/// Process the command entry
|
||||
void ProcessEntries(std::stop_token stop_token);
|
||||
|
|
@ -127,14 +127,14 @@ private:
|
|||
/// Invoke command class devices to execute the command based on the current state
|
||||
void ExecuteCommand(u32 state_offset, u32 data);
|
||||
|
||||
std::unique_ptr<Host1x::Control> host_processor;
|
||||
|
||||
std::mutex command_mutex;
|
||||
std::condition_variable_any command_cv;
|
||||
std::deque<ChCommandHeaderList> command_lists;
|
||||
std::jthread thread;
|
||||
|
||||
protected:
|
||||
ThiRegisters thi_regs{};
|
||||
std::deque<ChCommandHeaderList> command_lists;
|
||||
std::condition_variable_any command_cv;
|
||||
std::jthread thread;
|
||||
std::unique_ptr<Host1x::Control> host_processor;
|
||||
std::mutex command_mutex;
|
||||
Host1x::Host1x& host1x;
|
||||
ChClassId current_class;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ struct GPU::Impl {
|
|||
renderer = std::move(renderer_);
|
||||
rasterizer = renderer->ReadRasterizer();
|
||||
host1x.MemoryManager().BindInterface(rasterizer);
|
||||
host1x.GMMU().BindRasterizer(rasterizer);
|
||||
host1x.gmmu_manager.BindRasterizer(rasterizer);
|
||||
}
|
||||
|
||||
/// Flush all current written commands into the host GPU for execution.
|
||||
|
|
|
|||
542
src/video_core/host1x/codec_types.h
Normal file
542
src/video_core/host1x/codec_types.h
Normal file
|
|
@ -0,0 +1,542 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
namespace Decoders {
|
||||
|
||||
struct Offset {
|
||||
constexpr u32 Address() const noexcept {
|
||||
return offset << 8;
|
||||
}
|
||||
|
||||
private:
|
||||
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;
|
||||
};
|
||||
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(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_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(dpb, 0xC0);
|
||||
ASSERT_POSITION(weight_scale_4x4, 0x1C0);
|
||||
#undef ASSERT_POSITION
|
||||
|
||||
enum class Vp9SurfaceIndex : u32 {
|
||||
Last = 0,
|
||||
Golden = 1,
|
||||
AltRef = 2,
|
||||
Current = 3,
|
||||
};
|
||||
|
||||
struct Vp9FrameDimensions {
|
||||
s16 width;
|
||||
s16 height;
|
||||
s16 luma_pitch;
|
||||
s16 chroma_pitch;
|
||||
};
|
||||
static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size");
|
||||
|
||||
enum class FrameFlags : u32 {
|
||||
IsKeyFrame = 1 << 0,
|
||||
LastFrameIsKeyFrame = 1 << 1,
|
||||
FrameSizeChanged = 1 << 2,
|
||||
ErrorResilientMode = 1 << 3,
|
||||
LastShowFrame = 1 << 4,
|
||||
IntraOnly = 1 << 5,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(FrameFlags)
|
||||
|
||||
enum class TxSize {
|
||||
Tx4x4 = 0, // 4x4 transform
|
||||
Tx8x8 = 1, // 8x8 transform
|
||||
Tx16x16 = 2, // 16x16 transform
|
||||
Tx32x32 = 3, // 32x32 transform
|
||||
TxSizes = 4
|
||||
};
|
||||
|
||||
enum class TxMode {
|
||||
Only4X4 = 0, // Only 4x4 transform used
|
||||
Allow8X8 = 1, // Allow block transform size up to 8x8
|
||||
Allow16X16 = 2, // Allow block transform size up to 16x16
|
||||
Allow32X32 = 3, // Allow block transform size up to 32x32
|
||||
TxModeSelect = 4, // Transform specified for each block
|
||||
TxModes = 5
|
||||
};
|
||||
|
||||
struct Segmentation {
|
||||
constexpr bool operator==(const Segmentation& rhs) const = default;
|
||||
|
||||
u8 enabled;
|
||||
u8 update_map;
|
||||
u8 temporal_update;
|
||||
u8 abs_delta;
|
||||
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");
|
||||
|
||||
struct LoopFilter {
|
||||
u8 mode_ref_delta_enabled;
|
||||
std::array<s8, 4> ref_deltas;
|
||||
std::array<s8, 2> mode_deltas;
|
||||
};
|
||||
static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size");
|
||||
|
||||
struct Vp9EntropyProbs {
|
||||
std::array<u8, 36> y_mode_prob; ///< 0x0000
|
||||
std::array<u8, 64> partition_prob; ///< 0x0024
|
||||
std::array<u8, 1728> coef_probs; ///< 0x0064
|
||||
std::array<u8, 8> switchable_interp_prob; ///< 0x0724
|
||||
std::array<u8, 28> inter_mode_prob; ///< 0x072C
|
||||
std::array<u8, 4> intra_inter_prob; ///< 0x0748
|
||||
std::array<u8, 5> comp_inter_prob; ///< 0x074C
|
||||
std::array<u8, 10> single_ref_prob; ///< 0x0751
|
||||
std::array<u8, 5> comp_ref_prob; ///< 0x075B
|
||||
std::array<u8, 6> tx_32x32_prob; ///< 0x0760
|
||||
std::array<u8, 4> tx_16x16_prob; ///< 0x0766
|
||||
std::array<u8, 2> tx_8x8_prob; ///< 0x076A
|
||||
std::array<u8, 3> skip_probs; ///< 0x076C
|
||||
std::array<u8, 3> joints; ///< 0x076F
|
||||
std::array<u8, 2> sign; ///< 0x0772
|
||||
std::array<u8, 20> classes; ///< 0x0774
|
||||
std::array<u8, 2> class_0; ///< 0x0788
|
||||
std::array<u8, 20> prob_bits; ///< 0x078A
|
||||
std::array<u8, 12> class_0_fr; ///< 0x079E
|
||||
std::array<u8, 6> fr; ///< 0x07AA
|
||||
std::array<u8, 2> class_0_hp; ///< 0x07B0
|
||||
std::array<u8, 2> high_precision; ///< 0x07B2
|
||||
};
|
||||
static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size");
|
||||
|
||||
struct Vp9PictureInfo {
|
||||
u32 bitstream_size;
|
||||
std::array<u64, 4> frame_offsets;
|
||||
std::array<s8, 4> ref_frame_sign_bias;
|
||||
s32 base_q_index;
|
||||
s32 y_dc_delta_q;
|
||||
s32 uv_dc_delta_q;
|
||||
s32 uv_ac_delta_q;
|
||||
s32 transform_mode;
|
||||
s32 interp_filter;
|
||||
s32 reference_mode;
|
||||
s32 log2_tile_cols;
|
||||
s32 log2_tile_rows;
|
||||
std::array<s8, 4> ref_deltas;
|
||||
std::array<s8, 2> mode_deltas;
|
||||
Vp9EntropyProbs entropy;
|
||||
Vp9FrameDimensions frame_size;
|
||||
u8 first_level;
|
||||
u8 sharpness_level;
|
||||
bool is_key_frame;
|
||||
bool intra_only;
|
||||
bool last_frame_was_key;
|
||||
bool error_resilient_mode;
|
||||
bool last_frame_shown;
|
||||
bool show_frame;
|
||||
bool lossless;
|
||||
bool allow_high_precision_mv;
|
||||
bool segment_enabled;
|
||||
bool mode_ref_delta_enabled;
|
||||
};
|
||||
|
||||
struct Vp9FrameContainer {
|
||||
Vp9PictureInfo info{};
|
||||
std::vector<u8> bit_stream;
|
||||
};
|
||||
|
||||
struct PictureInfo {
|
||||
INSERT_PADDING_WORDS_NOINIT(12); ///< 0x00
|
||||
u32 bitstream_size; ///< 0x30
|
||||
INSERT_PADDING_WORDS_NOINIT(5); ///< 0x34
|
||||
Vp9FrameDimensions last_frame_size; ///< 0x48
|
||||
Vp9FrameDimensions golden_frame_size; ///< 0x50
|
||||
Vp9FrameDimensions alt_frame_size; ///< 0x58
|
||||
Vp9FrameDimensions current_frame_size; ///< 0x60
|
||||
FrameFlags vp9_flags; ///< 0x68
|
||||
std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C
|
||||
u8 first_level; ///< 0x70
|
||||
u8 sharpness_level; ///< 0x71
|
||||
u8 base_q_index; ///< 0x72
|
||||
u8 y_dc_delta_q; ///< 0x73
|
||||
u8 uv_ac_delta_q; ///< 0x74
|
||||
u8 uv_dc_delta_q; ///< 0x75
|
||||
u8 lossless; ///< 0x76
|
||||
u8 tx_mode; ///< 0x77
|
||||
u8 allow_high_precision_mv; ///< 0x78
|
||||
u8 interp_filter; ///< 0x79
|
||||
u8 reference_mode; ///< 0x7A
|
||||
INSERT_PADDING_BYTES_NOINIT(3); ///< 0x7B
|
||||
u8 log2_tile_cols; ///< 0x7E
|
||||
u8 log2_tile_rows; ///< 0x7F
|
||||
Segmentation segmentation; ///< 0x80
|
||||
LoopFilter loop_filter; ///< 0xE4
|
||||
INSERT_PADDING_BYTES_NOINIT(21); ///< 0xEB
|
||||
|
||||
[[nodiscard]] Vp9PictureInfo Convert() const {
|
||||
return {
|
||||
.bitstream_size = bitstream_size,
|
||||
.frame_offsets{},
|
||||
.ref_frame_sign_bias = ref_frame_sign_bias,
|
||||
.base_q_index = base_q_index,
|
||||
.y_dc_delta_q = y_dc_delta_q,
|
||||
.uv_dc_delta_q = uv_dc_delta_q,
|
||||
.uv_ac_delta_q = uv_ac_delta_q,
|
||||
.transform_mode = tx_mode,
|
||||
.interp_filter = interp_filter,
|
||||
.reference_mode = reference_mode,
|
||||
.log2_tile_cols = log2_tile_cols,
|
||||
.log2_tile_rows = log2_tile_rows,
|
||||
.ref_deltas = loop_filter.ref_deltas,
|
||||
.mode_deltas = loop_filter.mode_deltas,
|
||||
.entropy{},
|
||||
.frame_size = current_frame_size,
|
||||
.first_level = first_level,
|
||||
.sharpness_level = sharpness_level,
|
||||
.is_key_frame = True(vp9_flags & FrameFlags::IsKeyFrame),
|
||||
.intra_only = True(vp9_flags & FrameFlags::IntraOnly),
|
||||
.last_frame_was_key = True(vp9_flags & FrameFlags::LastFrameIsKeyFrame),
|
||||
.error_resilient_mode = True(vp9_flags & FrameFlags::ErrorResilientMode),
|
||||
.last_frame_shown = True(vp9_flags & FrameFlags::LastShowFrame),
|
||||
.show_frame = true,
|
||||
.lossless = lossless != 0,
|
||||
.allow_high_precision_mv = allow_high_precision_mv != 0,
|
||||
.segment_enabled = segmentation.enabled != 0,
|
||||
.mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size");
|
||||
|
||||
struct EntropyProbs {
|
||||
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
|
||||
std::array<u8, 2> tx_8x8_prob; ///< 0x0470
|
||||
std::array<u8, 4> tx_16x16_prob; ///< 0x0472
|
||||
std::array<u8, 6> tx_32x32_prob; ///< 0x0476
|
||||
std::array<u8, 4> y_mode_prob_e8; ///< 0x047C
|
||||
std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7; ///< 0x0480
|
||||
INSERT_PADDING_BYTES_NOINIT(64); ///< 0x04A0
|
||||
std::array<u8, 64> partition_prob; ///< 0x04E0
|
||||
INSERT_PADDING_BYTES_NOINIT(10); ///< 0x0520
|
||||
std::array<u8, 8> switchable_interp_prob; ///< 0x052A
|
||||
std::array<u8, 5> comp_inter_prob; ///< 0x0532
|
||||
std::array<u8, 3> skip_probs; ///< 0x0537
|
||||
INSERT_PADDING_BYTES_NOINIT(1); ///< 0x053A
|
||||
std::array<u8, 3> joints; ///< 0x053B
|
||||
std::array<u8, 2> sign; ///< 0x053E
|
||||
std::array<u8, 2> class_0; ///< 0x0540
|
||||
std::array<u8, 6> fr; ///< 0x0542
|
||||
std::array<u8, 2> class_0_hp; ///< 0x0548
|
||||
std::array<u8, 2> high_precision; ///< 0x054A
|
||||
std::array<u8, 20> classes; ///< 0x054C
|
||||
std::array<u8, 12> class_0_fr; ///< 0x0560
|
||||
std::array<u8, 20> pred_bits; ///< 0x056C
|
||||
std::array<u8, 10> single_ref_prob; ///< 0x0580
|
||||
std::array<u8, 5> comp_ref_prob; ///< 0x058A
|
||||
INSERT_PADDING_BYTES_NOINIT(17); ///< 0x058F
|
||||
std::array<u8, 2304> coef_probs; ///< 0x05A0
|
||||
|
||||
void Convert(Vp9EntropyProbs& fc) {
|
||||
fc.inter_mode_prob = inter_mode_prob;
|
||||
fc.intra_inter_prob = intra_inter_prob;
|
||||
fc.tx_8x8_prob = tx_8x8_prob;
|
||||
fc.tx_16x16_prob = tx_16x16_prob;
|
||||
fc.tx_32x32_prob = tx_32x32_prob;
|
||||
|
||||
for (std::size_t i = 0; i < 4; i++) {
|
||||
for (std::size_t j = 0; j < 9; j++) {
|
||||
fc.y_mode_prob[j + 9 * i] = j < 8 ? y_mode_prob_e0e7[i][j] : y_mode_prob_e8[i];
|
||||
}
|
||||
}
|
||||
|
||||
fc.partition_prob = partition_prob;
|
||||
fc.switchable_interp_prob = switchable_interp_prob;
|
||||
fc.comp_inter_prob = comp_inter_prob;
|
||||
fc.skip_probs = skip_probs;
|
||||
fc.joints = joints;
|
||||
fc.sign = sign;
|
||||
fc.class_0 = class_0;
|
||||
fc.fr = fr;
|
||||
fc.class_0_hp = class_0_hp;
|
||||
fc.high_precision = high_precision;
|
||||
fc.classes = classes;
|
||||
fc.class_0_fr = class_0_fr;
|
||||
fc.prob_bits = pred_bits;
|
||||
fc.single_ref_prob = single_ref_prob;
|
||||
fc.comp_ref_prob = comp_ref_prob;
|
||||
|
||||
// Skip the 4th element as it goes unused
|
||||
for (std::size_t i = 0; i < coef_probs.size(); i += 4) {
|
||||
const std::size_t j = i - i / 4;
|
||||
fc.coef_probs[j] = coef_probs[i];
|
||||
fc.coef_probs[j + 1] = coef_probs[i + 1];
|
||||
fc.coef_probs[j + 2] = coef_probs[i + 2];
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(EntropyProbs) == 0xEA0, "EntropyProbs is an invalid size");
|
||||
|
||||
enum class Ref { Last, Golden, AltRef };
|
||||
|
||||
struct RefPoolElement {
|
||||
s64 frame{};
|
||||
Ref ref{};
|
||||
bool refresh{};
|
||||
};
|
||||
|
||||
#define ASSERT_POSITION(field_name, position) static_assert(offsetof(Vp9EntropyProbs, field_name) == position)
|
||||
ASSERT_POSITION(partition_prob, 0x0024);
|
||||
ASSERT_POSITION(switchable_interp_prob, 0x0724);
|
||||
ASSERT_POSITION(sign, 0x0772);
|
||||
ASSERT_POSITION(class_0_fr, 0x079E);
|
||||
ASSERT_POSITION(high_precision, 0x07B2);
|
||||
#undef ASSERT_POSITION
|
||||
|
||||
#define ASSERT_POSITION(field_name, position) static_assert(offsetof(PictureInfo, field_name) == position)
|
||||
ASSERT_POSITION(bitstream_size, 0x30);
|
||||
ASSERT_POSITION(last_frame_size, 0x48);
|
||||
ASSERT_POSITION(first_level, 0x70);
|
||||
ASSERT_POSITION(segmentation, 0x80);
|
||||
ASSERT_POSITION(loop_filter, 0xE4);
|
||||
#undef ASSERT_POSITION
|
||||
|
||||
#define ASSERT_POSITION(field_name, position) static_assert(offsetof(EntropyProbs, field_name) == position)
|
||||
|
||||
ASSERT_POSITION(inter_mode_prob, 0x400);
|
||||
ASSERT_POSITION(tx_8x8_prob, 0x470);
|
||||
ASSERT_POSITION(partition_prob, 0x4E0);
|
||||
ASSERT_POSITION(class_0, 0x540);
|
||||
ASSERT_POSITION(class_0_fr, 0x560);
|
||||
ASSERT_POSITION(coef_probs, 0x5A0);
|
||||
#undef ASSERT_POSITION
|
||||
|
||||
struct VP8PictureInfo {
|
||||
INSERT_PADDING_WORDS_NOINIT(14);
|
||||
u16 frame_width; // actual frame width
|
||||
u16 frame_height; // actual frame height
|
||||
u8 key_frame;
|
||||
u8 version;
|
||||
union {
|
||||
u8 raw;
|
||||
BitField<0, 2, u8> tile_format;
|
||||
BitField<2, 3, u8> gob_height;
|
||||
BitField<5, 3, u8> reserved_surface_format;
|
||||
};
|
||||
u8 error_conceal_on; // 1: error conceal on; 0: off
|
||||
u32 first_part_size; // the size of first partition(frame header and mb header partition)
|
||||
u32 hist_buffer_size; // in units of 256
|
||||
u32 vld_buffer_size; // in units of 1
|
||||
// Current frame buffers
|
||||
std::array<u32, 2> frame_stride; // [y_c]
|
||||
u32 luma_top_offset; // offset of luma top field in units of 256
|
||||
u32 luma_bot_offset; // offset of luma bottom field in units of 256
|
||||
u32 luma_frame_offset; // offset of luma frame in units of 256
|
||||
u32 chroma_top_offset; // offset of chroma top field in units of 256
|
||||
u32 chroma_bot_offset; // offset of chroma bottom field in units of 256
|
||||
u32 chroma_frame_offset; // offset of chroma frame in units of 256
|
||||
|
||||
INSERT_PADDING_BYTES_NOINIT(0x1c); // NvdecDisplayParams
|
||||
|
||||
// Decode picture buffer related
|
||||
s8 current_output_memory_layout;
|
||||
// output NV12/NV24 setting. index 0: golden; 1: altref; 2: last
|
||||
std::array<s8, 3> output_memory_layout;
|
||||
|
||||
u8 segmentation_feature_data_update;
|
||||
INSERT_PADDING_BYTES_NOINIT(3);
|
||||
|
||||
// ucode return result
|
||||
u32 result_value;
|
||||
std::array<u32, 8> partition_offset;
|
||||
INSERT_PADDING_WORDS_NOINIT(3);
|
||||
};
|
||||
static_assert(sizeof(VP8PictureInfo) == 0xc0, "PictureInfo is an invalid size");
|
||||
|
||||
}; // namespace Decoders
|
||||
}; // namespace Tegra
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -13,9 +16,12 @@
|
|||
namespace Tegra {
|
||||
|
||||
Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs)
|
||||
: host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)),
|
||||
vp8_decoder(std::make_unique<Decoder::VP8>(host1x)),
|
||||
vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {}
|
||||
: host1x(host1x_)
|
||||
, state{regs}
|
||||
, h264_decoder(host1x_)
|
||||
, vp8_decoder(host1x_)
|
||||
, vp9_decoder(host1x_)
|
||||
{}
|
||||
|
||||
Codec::~Codec() = default;
|
||||
|
||||
|
|
@ -32,13 +38,11 @@ void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
|
|||
|
||||
void Codec::Decode() {
|
||||
const bool is_first_frame = !initialized;
|
||||
if (is_first_frame) {
|
||||
if (is_first_frame)
|
||||
Initialize();
|
||||
}
|
||||
|
||||
if (!initialized) {
|
||||
if (!initialized)
|
||||
return;
|
||||
}
|
||||
|
||||
// Assemble bitstream.
|
||||
bool vp9_hidden_frame = false;
|
||||
|
|
@ -46,13 +50,13 @@ void Codec::Decode() {
|
|||
const auto packet_data = [&]() {
|
||||
switch (current_codec) {
|
||||
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
|
||||
return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame);
|
||||
return h264_decoder.ComposeFrame(state, &configuration_size, is_first_frame);
|
||||
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
|
||||
return vp8_decoder->ComposeFrame(state);
|
||||
return vp8_decoder.ComposeFrame(state);
|
||||
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
|
||||
vp9_decoder->ComposeFrame(state);
|
||||
vp9_hidden_frame = vp9_decoder->WasFrameHidden();
|
||||
return vp9_decoder->GetFrameBytes();
|
||||
vp9_decoder.ComposeFrame(state);
|
||||
vp9_hidden_frame = vp9_decoder.WasFrameHidden();
|
||||
return vp9_decoder.GetFrameBytes();
|
||||
default:
|
||||
ASSERT(false);
|
||||
return std::span<const u8>{};
|
||||
|
|
@ -81,19 +85,13 @@ void Codec::Decode() {
|
|||
std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() {
|
||||
// Sometimes VIC will request more frames than have been decoded.
|
||||
// in this case, return a blank frame and don't overwrite previous data.
|
||||
if (frames.empty()) {
|
||||
if (frames.empty())
|
||||
return {};
|
||||
}
|
||||
|
||||
auto frame = std::move(frames.front());
|
||||
frames.pop();
|
||||
return frame;
|
||||
}
|
||||
|
||||
Host1x::NvdecCommon::VideoCodec Codec::GetCurrentCodec() const {
|
||||
return current_codec;
|
||||
}
|
||||
|
||||
std::string_view Codec::GetCurrentCodecName() const {
|
||||
switch (current_codec) {
|
||||
case Host1x::NvdecCommon::VideoCodec::None:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -8,17 +11,14 @@
|
|||
#include <string_view>
|
||||
#include <queue>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/host1x/ffmpeg/ffmpeg.h"
|
||||
#include "video_core/host1x/codecs/h264.h"
|
||||
#include "video_core/host1x/codecs/vp8.h"
|
||||
#include "video_core/host1x/codecs/vp9.h"
|
||||
#include "video_core/host1x/ffmpeg.h"
|
||||
#include "video_core/host1x/nvdec_common.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
namespace Decoder {
|
||||
class H264;
|
||||
class VP8;
|
||||
class VP9;
|
||||
} // namespace Decoder
|
||||
|
||||
namespace Host1x {
|
||||
class Host1x;
|
||||
} // namespace Host1x
|
||||
|
|
@ -40,9 +40,6 @@ public:
|
|||
/// Returns next decoded frame
|
||||
[[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame();
|
||||
|
||||
/// Returns the value of current_codec
|
||||
[[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const;
|
||||
|
||||
/// Return name of the current codec
|
||||
[[nodiscard]] std::string_view GetCurrentCodecName() const;
|
||||
|
||||
|
|
@ -53,10 +50,9 @@ private:
|
|||
|
||||
Host1x::Host1x& host1x;
|
||||
const Host1x::NvdecCommon::NvdecRegisters& state;
|
||||
std::unique_ptr<Decoder::H264> h264_decoder;
|
||||
std::unique_ptr<Decoder::VP8> vp8_decoder;
|
||||
std::unique_ptr<Decoder::VP9> vp9_decoder;
|
||||
|
||||
Decoders::H264 h264_decoder;
|
||||
Decoders::VP8 vp8_decoder;
|
||||
Decoders::VP9 vp9_decoder;
|
||||
std::queue<std::unique_ptr<FFmpeg::Frame>> frames{};
|
||||
};
|
||||
|
||||
|
|
|
|||
23
src/video_core/host1x/codecs/decoder.cpp
Executable file → Normal file
23
src/video_core/host1x/codecs/decoder.cpp
Executable file → Normal file
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
|
|
@ -12,10 +12,11 @@
|
|||
|
||||
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(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::NvdecRegisters& regs_)
|
||||
: host1x(host1x_)
|
||||
, regs{regs_}
|
||||
, id{id_}
|
||||
{}
|
||||
|
||||
Decoder::~Decoder() = default;
|
||||
|
||||
|
|
@ -53,11 +54,11 @@ void Decoder::Decode() {
|
|||
}
|
||||
|
||||
if (UsingDecodeOrder()) {
|
||||
frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
|
||||
frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy));
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
|
||||
host1x.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));
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_top, std::move(frame));
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy));
|
||||
}
|
||||
} else {
|
||||
auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
|
||||
|
|
@ -68,9 +69,9 @@ void Decoder::Decode() {
|
|||
}
|
||||
|
||||
if (UsingDecodeOrder()) {
|
||||
frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame));
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame));
|
||||
} else {
|
||||
frame_queue.PushPresentOrder(id, luma_offset, std::move(frame));
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_offset, std::move(frame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include <queue>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/host1x/ffmpeg/ffmpeg.h"
|
||||
#include "video_core/host1x/ffmpeg.h"
|
||||
#include "video_core/host1x/nvdec_common.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
|
@ -35,33 +35,23 @@ public:
|
|||
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);
|
||||
explicit Decoder(Host1x::Host1x& host1x, s32 id, const Host1x::NvdecCommon::NvdecRegisters& regs);
|
||||
|
||||
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;
|
||||
|
||||
FFmpeg::DecodeApi decode_api;
|
||||
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{};
|
||||
bool initialized : 1 = false;
|
||||
bool vp9_hidden_frame : 1 = false;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
|
|
@ -14,25 +14,11 @@
|
|||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra::Decoders {
|
||||
namespace {
|
||||
// ZigZag LUTs from libavcodec.
|
||||
constexpr std::array<u8, 64> zig_zag_direct{
|
||||
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48,
|
||||
41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23,
|
||||
30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
|
||||
};
|
||||
|
||||
constexpr std::array<u8, 16> zig_zag_scan{
|
||||
0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4, 1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4,
|
||||
1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4, 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4,
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
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(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_)
|
||||
: Decoder{host1x_, id_, regs_}
|
||||
{
|
||||
initialized = decode_api.Initialize(Host1x::NvdecCommon::VideoCodec::H264);
|
||||
}
|
||||
|
||||
H264::~H264() = default;
|
||||
|
|
@ -65,14 +51,11 @@ bool H264::IsInterlaced() {
|
|||
}
|
||||
|
||||
std::span<const u8> H264::ComposeFrame() {
|
||||
memory_manager.ReadBlock(regs.picture_info_offset.Address(), ¤t_context,
|
||||
sizeof(H264DecoderContext));
|
||||
|
||||
host1x.memory_manager.ReadBlock(regs.picture_info_offset.Address(), ¤t_context, sizeof(H264DecoderContext));
|
||||
const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
|
||||
if (!is_first_frame && frame_number != 0) {
|
||||
frame_scratch.resize_destructive(current_context.stream_len);
|
||||
memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(), frame_scratch.data(),
|
||||
frame_scratch.size());
|
||||
host1x.memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(), frame_scratch.data(), frame_scratch.size());
|
||||
return frame_scratch;
|
||||
}
|
||||
|
||||
|
|
@ -174,15 +157,13 @@ std::span<const u8> H264::ComposeFrame() {
|
|||
|
||||
for (s32 index = 0; index < 6; index++) {
|
||||
writer.WriteBit(true);
|
||||
std::span<const u8> matrix{current_context.weight_scale_4x4};
|
||||
writer.WriteScalingList(scan_scratch, matrix, index * 16, 16);
|
||||
writer.WriteScalingList(current_context.weight_scale_4x4, index * 16, 16);
|
||||
}
|
||||
|
||||
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{current_context.weight_scale_8x8};
|
||||
writer.WriteScalingList(scan_scratch, matrix, index * 64, 64);
|
||||
writer.WriteScalingList(current_context.weight_scale_8x8, index * 64, 64);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,11 +177,7 @@ std::span<const u8> H264::ComposeFrame() {
|
|||
const auto& encoded_header = writer.GetByteArray();
|
||||
frame_scratch.resize(encoded_header.size() + current_context.stream_len);
|
||||
std::memcpy(frame_scratch.data(), encoded_header.data(), encoded_header.size());
|
||||
|
||||
memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(),
|
||||
frame_scratch.data() + encoded_header.size(),
|
||||
current_context.stream_len);
|
||||
|
||||
host1x.memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(), frame_scratch.data() + encoded_header.size(), current_context.stream_len);
|
||||
return frame_scratch;
|
||||
}
|
||||
|
||||
|
|
@ -229,23 +206,37 @@ void H264BitWriter::WriteBit(bool state) {
|
|||
WriteBits(state ? 1 : 0, 1);
|
||||
}
|
||||
|
||||
void H264BitWriter::WriteScalingList(Common::ScratchBuffer<u8>& scan, std::span<const u8> list,
|
||||
s32 start, s32 count) {
|
||||
scan.resize_destructive(count);
|
||||
void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) {
|
||||
if (count == 16) {
|
||||
std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());
|
||||
u8 last_scale = 8;
|
||||
for (s32 index = 0; index < count; index++) {
|
||||
// libavcodec has a zig zag LUT, but we dont need it, just use a magic
|
||||
// constant which is a packing of 4 bits for each component of the table
|
||||
const u8 value = list[start + ((0xfeb7adc963258410 >> (index * 4)) & 0xf)];
|
||||
const s32 delta_scale = s32(value - last_scale);
|
||||
WriteSe(delta_scale);
|
||||
last_scale = value;
|
||||
}
|
||||
} else {
|
||||
std::memcpy(scan.data(), zig_zag_direct.data(), scan.size());
|
||||
}
|
||||
u8 last_scale = 8;
|
||||
|
||||
for (s32 index = 0; index < count; index++) {
|
||||
const u8 value = list[start + scan[index]];
|
||||
const s32 delta_scale = static_cast<s32>(value - last_scale);
|
||||
|
||||
WriteSe(delta_scale);
|
||||
|
||||
last_scale = value;
|
||||
// ZigZag LUTs from libavcodec: this is the famous zigzag pattern found in the ffmpeg logo itself!
|
||||
static constexpr std::array<u8, 64> scan{
|
||||
0, 1, 8, 16, 9, 2, 3, 10,
|
||||
17, 24, 32, 25, 18, 11, 4,
|
||||
5, 12, 19, 26, 33, 40, 48,
|
||||
41, 34, 27, 20, 13, 6, 7,
|
||||
14, 21, 28, 35, 42, 49, 56,
|
||||
57, 50, 43, 36, 29, 22, 15,
|
||||
23, 30, 37, 44, 51, 58, 59,
|
||||
52, 45, 38, 31, 39, 46, 53,
|
||||
60, 61, 54, 47, 55, 62, 63,
|
||||
};
|
||||
u8 last_scale = 8;
|
||||
for (s32 index = 0; index < count; index++) {
|
||||
const u8 value = list[start + scan[index]];
|
||||
const s32 delta_scale = s32(value - last_scale);
|
||||
WriteSe(delta_scale);
|
||||
last_scale = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -286,19 +277,15 @@ void H264BitWriter::WriteBits(s32 value, s32 bit_count) {
|
|||
|
||||
void H264BitWriter::WriteExpGolombCodedInt(s32 value) {
|
||||
const s32 sign = value <= 0 ? 0 : 1;
|
||||
if (value < 0) {
|
||||
value = -value;
|
||||
}
|
||||
value = (value << 1) - sign;
|
||||
WriteExpGolombCodedUInt(value);
|
||||
if (!sign) value = -value;
|
||||
WriteExpGolombCodedUInt((value << 1) - sign);
|
||||
}
|
||||
|
||||
void H264BitWriter::WriteExpGolombCodedUInt(u32 value) {
|
||||
const s32 size = 32 - std::countl_zero(value + 1);
|
||||
WriteBits(1, size);
|
||||
|
||||
value -= (1U << (size - 1)) - 1;
|
||||
WriteBits(static_cast<s32>(value), size - 1);
|
||||
WriteBits(s32(value), size - 1);
|
||||
}
|
||||
|
||||
s32 H264BitWriter::GetFreeBufferBits() {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -6,11 +9,10 @@
|
|||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#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/codec_types.h"
|
||||
#include "video_core/host1x/nvdec_common.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
|
@ -40,8 +42,7 @@ public:
|
|||
|
||||
/// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification
|
||||
/// Writes the scaling matrices of the sream
|
||||
void WriteScalingList(Common::ScratchBuffer<u8>& scan, std::span<const u8> list, s32 start,
|
||||
s32 count);
|
||||
void WriteScalingList(std::span<const u8> list, s32 start, s32 count);
|
||||
|
||||
/// Return the bitstream as a vector.
|
||||
[[nodiscard]] std::vector<u8>& GetByteArray();
|
||||
|
|
@ -61,188 +62,9 @@ private:
|
|||
std::vector<u8> byte_array;
|
||||
};
|
||||
|
||||
struct Offset {
|
||||
constexpr u32 Address() const noexcept {
|
||||
return offset << 8;
|
||||
}
|
||||
|
||||
private:
|
||||
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;
|
||||
};
|
||||
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(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_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(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);
|
||||
explicit H264(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id);
|
||||
~H264() override;
|
||||
|
||||
H264(const H264&) = delete;
|
||||
|
|
@ -263,10 +85,10 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
bool is_first_frame{true};
|
||||
Common::ScratchBuffer<u8> frame_scratch;
|
||||
Common::ScratchBuffer<u8> scan_scratch;
|
||||
H264DecoderContext current_context{};
|
||||
std::array<u8, 64> scan_scratch;
|
||||
Common::ScratchBuffer<u8> frame_scratch;
|
||||
bool is_first_frame{true};
|
||||
};
|
||||
|
||||
} // namespace Decoders
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -8,11 +11,10 @@
|
|||
#include "video_core/memory_manager.h"
|
||||
|
||||
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(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_)
|
||||
: Decoder{host1x_, id_, regs_}
|
||||
{
|
||||
initialized = decode_api.Initialize(Host1x::NvdecCommon::VideoCodec::VP8);
|
||||
}
|
||||
|
||||
VP8::~VP8() = default;
|
||||
|
|
@ -25,35 +27,30 @@ std::tuple<u64, u64> VP8::GetProgressiveOffsets() {
|
|||
|
||||
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()};
|
||||
auto luma_bottom = regs.surface_luma_offsets[u32(Vp8SurfaceIndex::Current)].Address();
|
||||
auto chroma_top = regs.surface_chroma_offsets[u32(Vp8SurfaceIndex::Current)].Address();
|
||||
auto chroma_bottom = regs.surface_chroma_offsets[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(), ¤t_context,
|
||||
sizeof(VP8PictureInfo));
|
||||
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), ¤t_context, sizeof(VP8PictureInfo));
|
||||
|
||||
const bool is_key_frame = current_context.key_frame == 1u;
|
||||
const auto bitstream_size = static_cast<size_t>(current_context.vld_buffer_size);
|
||||
const auto bitstream_size = size_t(current_context.vld_buffer_size);
|
||||
const size_t header_size = is_key_frame ? 10u : 3u;
|
||||
frame_scratch.resize(header_size + bitstream_size);
|
||||
|
||||
// Based on page 30 of the VP8 specification.
|
||||
// https://datatracker.ietf.org/doc/rfc6386/
|
||||
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
|
||||
frame_scratch[0] |= u8((current_context.version & 7u) << 1u); // 3-bit version number
|
||||
frame_scratch[0] |= u8(1u << 4u); // 1-bit show_frame flag
|
||||
|
||||
// The next 19-bits are the first partition size
|
||||
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);
|
||||
frame_scratch[0] |= u8((current_context.first_part_size & 7u) << 5u);
|
||||
frame_scratch[1] = u8((current_context.first_part_size & 0x7f8u) >> 3u);
|
||||
frame_scratch[2] = u8((current_context.first_part_size & 0x7f800u) >> 11u);
|
||||
|
||||
if (is_key_frame) {
|
||||
frame_scratch[3] = 0x9du;
|
||||
|
|
@ -61,15 +58,15 @@ std::span<const u8> VP8::ComposeFrame() {
|
|||
frame_scratch[5] = 0x2au;
|
||||
// TODO(ameerj): Horizontal/Vertical Scale
|
||||
// 16 bits: (2 bits Horizontal Scale << 14) | Width (14 bits)
|
||||
frame_scratch[6] = static_cast<u8>(current_context.frame_width & 0xff);
|
||||
frame_scratch[7] = static_cast<u8>(((current_context.frame_width >> 8) & 0x3f));
|
||||
frame_scratch[6] = u8(current_context.frame_width & 0xff);
|
||||
frame_scratch[7] = u8(((current_context.frame_width >> 8) & 0x3f));
|
||||
// 16 bits:(2 bits Vertical Scale << 14) | Height (14 bits)
|
||||
frame_scratch[8] = static_cast<u8>(current_context.frame_height & 0xff);
|
||||
frame_scratch[9] = static_cast<u8>(((current_context.frame_height >> 8) & 0x3f));
|
||||
frame_scratch[8] = u8(current_context.frame_height & 0xff);
|
||||
frame_scratch[9] = u8(((current_context.frame_height >> 8) & 0x3f));
|
||||
}
|
||||
const u64 bitstream_offset = regs.frame_bitstream_offset.Address();
|
||||
memory_manager.ReadBlock(bitstream_offset, frame_scratch.data() + header_size, bitstream_size);
|
||||
|
||||
const u64 bitstream_offset = regs.frame_bitstream_offset.Address();
|
||||
host1x.gmmu_manager.ReadBlock(bitstream_offset, frame_scratch.data() + header_size, bitstream_size);
|
||||
return frame_scratch;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -11,6 +14,7 @@
|
|||
#include "common/scratch_buffer.h"
|
||||
#include "video_core/host1x/codecs/decoder.h"
|
||||
#include "video_core/host1x/nvdec_common.h"
|
||||
#include "video_core/host1x/codec_types.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
|
|
@ -28,8 +32,7 @@ enum class Vp8SurfaceIndex : u32 {
|
|||
|
||||
class VP8 final : public Decoder {
|
||||
public:
|
||||
explicit VP8(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
|
||||
Host1x::FrameQueue& frame_queue);
|
||||
explicit VP8(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id);
|
||||
~VP8() override;
|
||||
|
||||
VP8(const VP8&) = delete;
|
||||
|
|
@ -52,51 +55,8 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
Common::ScratchBuffer<u8> frame_scratch;
|
||||
|
||||
struct VP8PictureInfo {
|
||||
INSERT_PADDING_WORDS_NOINIT(14);
|
||||
u16 frame_width; // actual frame width
|
||||
u16 frame_height; // actual frame height
|
||||
u8 key_frame;
|
||||
u8 version;
|
||||
union {
|
||||
u8 raw;
|
||||
BitField<0, 2, u8> tile_format;
|
||||
BitField<2, 3, u8> gob_height;
|
||||
BitField<5, 3, u8> reserved_surface_format;
|
||||
};
|
||||
u8 error_conceal_on; // 1: error conceal on; 0: off
|
||||
u32 first_part_size; // the size of first partition(frame header and mb header partition)
|
||||
u32 hist_buffer_size; // in units of 256
|
||||
u32 vld_buffer_size; // in units of 1
|
||||
// Current frame buffers
|
||||
std::array<u32, 2> frame_stride; // [y_c]
|
||||
u32 luma_top_offset; // offset of luma top field in units of 256
|
||||
u32 luma_bot_offset; // offset of luma bottom field in units of 256
|
||||
u32 luma_frame_offset; // offset of luma frame in units of 256
|
||||
u32 chroma_top_offset; // offset of chroma top field in units of 256
|
||||
u32 chroma_bot_offset; // offset of chroma bottom field in units of 256
|
||||
u32 chroma_frame_offset; // offset of chroma frame in units of 256
|
||||
|
||||
INSERT_PADDING_BYTES_NOINIT(0x1c); // NvdecDisplayParams
|
||||
|
||||
// Decode picture buffer related
|
||||
s8 current_output_memory_layout;
|
||||
// output NV12/NV24 setting. index 0: golden; 1: altref; 2: last
|
||||
std::array<s8, 3> output_memory_layout;
|
||||
|
||||
u8 segmentation_feature_data_update;
|
||||
INSERT_PADDING_BYTES_NOINIT(3);
|
||||
|
||||
// ucode return result
|
||||
u32 result_value;
|
||||
std::array<u32, 8> partition_offset;
|
||||
INSERT_PADDING_WORDS_NOINIT(3);
|
||||
};
|
||||
static_assert(sizeof(VP8PictureInfo) == 0xc0, "PictureInfo is an invalid size");
|
||||
|
||||
VP8PictureInfo current_context{};
|
||||
Common::ScratchBuffer<u8> frame_scratch;
|
||||
};
|
||||
|
||||
} // namespace Decoders
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm> // for std::copy
|
||||
#include <bit>
|
||||
#include <numeric>
|
||||
|
||||
#include "common/alignment.h"
|
||||
|
|
@ -15,11 +16,11 @@
|
|||
|
||||
namespace Tegra::Decoders {
|
||||
namespace {
|
||||
constexpr u32 diff_update_probability = 252;
|
||||
constexpr u32 frame_sync_code = 0x498342;
|
||||
static constexpr u32 diff_update_probability = 252;
|
||||
static constexpr u32 frame_sync_code = 0x498342;
|
||||
|
||||
// Default compressed header probabilities once frame context resets
|
||||
constexpr Vp9EntropyProbs default_probs{
|
||||
static constexpr Vp9EntropyProbs default_probs{
|
||||
.y_mode_prob{
|
||||
65, 32, 18, 144, 162, 194, 41, 51, 98, 132, 68, 18, 165, 217, 196, 45, 40, 78,
|
||||
173, 80, 19, 176, 240, 193, 64, 35, 46, 221, 135, 38, 194, 248, 121, 96, 85, 29,
|
||||
|
|
@ -158,34 +159,6 @@ constexpr Vp9EntropyProbs default_probs{
|
|||
.high_precision{128, 128},
|
||||
};
|
||||
|
||||
constexpr std::array<u8, 256> norm_lut{
|
||||
0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
constexpr std::array<u8, 254> map_lut{
|
||||
20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
|
||||
1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54,
|
||||
55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
|
||||
73, 4, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 5, 86, 87, 88, 89,
|
||||
90, 91, 92, 93, 94, 95, 96, 97, 6, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
|
||||
108, 109, 7, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 8, 122, 123, 124,
|
||||
125, 126, 127, 128, 129, 130, 131, 132, 133, 9, 134, 135, 136, 137, 138, 139, 140, 141, 142,
|
||||
143, 144, 145, 10, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 11, 158, 159,
|
||||
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 12, 170, 171, 172, 173, 174, 175, 176, 177,
|
||||
178, 179, 180, 181, 13, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 14, 194,
|
||||
195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 15, 206, 207, 208, 209, 210, 211, 212,
|
||||
213, 214, 215, 216, 217, 16, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 17,
|
||||
230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 18, 242, 243, 244, 245, 246, 247,
|
||||
248, 249, 250, 251, 252, 253, 19,
|
||||
};
|
||||
|
||||
// 6.2.14 Tile size calculation
|
||||
|
||||
[[nodiscard]] s32 CalcMinLog2TileCols(s32 frame_width) {
|
||||
|
|
@ -227,25 +200,17 @@ constexpr std::array<u8, 254> map_lut{
|
|||
[[nodiscard]] s32 RemapProbability(s32 new_prob, s32 old_prob) {
|
||||
new_prob--;
|
||||
old_prob--;
|
||||
|
||||
std::size_t index{};
|
||||
|
||||
if (old_prob * 2 <= 0xff) {
|
||||
index = static_cast<std::size_t>((std::max)(0, RecenterNonNeg(new_prob, old_prob) - 1));
|
||||
} else {
|
||||
index = static_cast<std::size_t>(
|
||||
(std::max)(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
|
||||
}
|
||||
|
||||
return static_cast<s32>(map_lut[index]);
|
||||
u8 i = old_prob * 2 <= 0xff
|
||||
? u8((std::max)(0, RecenterNonNeg(new_prob, old_prob) - 1))
|
||||
: u8((std::max)(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
|
||||
return s32((i + 7) % 13 == 0 ? (i + 7) / 13 - 1 : i + 20 - (i + 7) / 13);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
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(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_)
|
||||
: Decoder{host1x_, id_, regs_}
|
||||
{
|
||||
initialized = decode_api.Initialize(Host1x::NvdecCommon::VideoCodec::VP9);
|
||||
}
|
||||
|
||||
VP9::~VP9() = default;
|
||||
|
|
@ -377,8 +342,7 @@ void VP9::WriteSegmentation(VpxBitStreamWriter& writer) {
|
|||
|
||||
if (update_map) {
|
||||
EntropyProbs entropy_probs{};
|
||||
memory_manager.ReadBlock(regs.vp9_prob_tab_buffer_offset.Address(), &entropy_probs,
|
||||
sizeof(entropy_probs));
|
||||
host1x.gmmu_manager.ReadBlock(regs.vp9_prob_tab_buffer_offset.Address(), &entropy_probs, sizeof(entropy_probs));
|
||||
|
||||
auto WriteProb = [&](u8 prob) {
|
||||
bool coded = prob != 255;
|
||||
|
|
@ -442,8 +406,7 @@ void VP9::WriteSegmentation(VpxBitStreamWriter& writer) {
|
|||
}
|
||||
|
||||
Vp9PictureInfo VP9::GetVp9PictureInfo() {
|
||||
memory_manager.ReadBlock(regs.picture_info_offset.Address(), ¤t_picture_info,
|
||||
sizeof(PictureInfo));
|
||||
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), ¤t_picture_info, sizeof(PictureInfo));
|
||||
Vp9PictureInfo vp9_info = current_picture_info.Convert();
|
||||
|
||||
InsertEntropy(regs.vp9_prob_tab_buffer_offset.Address(), vp9_info.entropy);
|
||||
|
|
@ -459,7 +422,7 @@ Vp9PictureInfo VP9::GetVp9PictureInfo() {
|
|||
|
||||
void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
|
||||
EntropyProbs entropy;
|
||||
memory_manager.ReadBlock(offset, &entropy, sizeof(EntropyProbs));
|
||||
host1x.gmmu_manager.ReadBlock(offset, &entropy, sizeof(EntropyProbs));
|
||||
entropy.Convert(dst);
|
||||
}
|
||||
|
||||
|
|
@ -469,9 +432,7 @@ Vp9FrameContainer VP9::GetCurrentFrame() {
|
|||
// gpu.SyncGuestHost(); epic, why?
|
||||
current_frame.info = GetVp9PictureInfo();
|
||||
current_frame.bit_stream.resize(current_frame.info.bitstream_size);
|
||||
memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(),
|
||||
current_frame.bit_stream.data(),
|
||||
current_frame.info.bitstream_size);
|
||||
host1x.gmmu_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{
|
||||
|
|
@ -893,13 +854,9 @@ std::span<const u8> VP9::ComposeFrame() {
|
|||
// Write headers and frame to buffer
|
||||
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_scratch.begin() + uncompressed_header.size());
|
||||
std::copy(bitstream.begin(), bitstream.end(),
|
||||
frame_scratch.begin() + uncompressed_header.size() + compressed_header.size());
|
||||
|
||||
std::copy(compressed_header.begin(), compressed_header.end(), frame_scratch.begin() + uncompressed_header.size());
|
||||
std::copy(bitstream.begin(), bitstream.end(), frame_scratch.begin() + uncompressed_header.size() + compressed_header.size());
|
||||
vp9_hidden_frame = WasFrameHidden();
|
||||
|
||||
return GetFrameBytes();
|
||||
}
|
||||
|
||||
|
|
@ -929,7 +886,7 @@ void VpxRangeEncoder::Write(bool bit, s32 probability) {
|
|||
local_range = range - split;
|
||||
}
|
||||
|
||||
s32 shift = static_cast<s32>(norm_lut[local_range]);
|
||||
s32 shift = s32(local_range == 0 ? 0 : (std::countl_zero<uint32_t>(local_range) - 24));
|
||||
local_range <<= shift;
|
||||
count += shift;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -11,7 +14,7 @@
|
|||
#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/codec_types.h"
|
||||
#include "video_core/host1x/nvdec_common.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
|
@ -113,8 +116,7 @@ private:
|
|||
|
||||
class VP9 final : public Decoder {
|
||||
public:
|
||||
explicit VP9(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
|
||||
Host1x::FrameQueue& frame_queue);
|
||||
explicit VP9(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id);
|
||||
~VP9() override;
|
||||
|
||||
VP9(const VP9&) = delete;
|
||||
|
|
@ -193,11 +195,10 @@ private:
|
|||
[[nodiscard]] std::vector<u8> ComposeCompressedHeader();
|
||||
[[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader();
|
||||
|
||||
Common::ScratchBuffer<u8> frame_scratch;
|
||||
|
||||
std::array<s8, 4> loop_filter_ref_deltas{};
|
||||
std::array<s8, 2> loop_filter_mode_deltas{};
|
||||
|
||||
Common::ScratchBuffer<u8> frame_scratch;
|
||||
Vp9FrameContainer next_frame{};
|
||||
std::array<Vp9EntropyProbs, 4> frame_ctxs{};
|
||||
bool swap_ref_indices{};
|
||||
|
|
|
|||
316
src/video_core/host1x/ffmpeg.cpp
Normal file
316
src/video_core/host1x/ffmpeg.cpp
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/host1x/ffmpeg.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
extern "C" {
|
||||
#ifdef LIBVA_FOUND
|
||||
// for querying VAAPI driver information
|
||||
#include <libavutil/hwcontext_vaapi.h>
|
||||
#endif
|
||||
|
||||
#include <libavutil/hwcontext.h>
|
||||
}
|
||||
|
||||
namespace FFmpeg {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12;
|
||||
constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P;
|
||||
constexpr std::array PreferredGpuDecoders = {
|
||||
#if defined (_WIN32)
|
||||
AV_HWDEVICE_TYPE_CUDA,
|
||||
AV_HWDEVICE_TYPE_D3D11VA,
|
||||
AV_HWDEVICE_TYPE_DXVA2,
|
||||
AV_HWDEVICE_TYPE_D3D12VA,
|
||||
#elif defined(__FreeBSD__)
|
||||
AV_HWDEVICE_TYPE_VAAPI,
|
||||
AV_HWDEVICE_TYPE_VDPAU,
|
||||
AV_HWDEVICE_TYPE_DRM,
|
||||
#elif defined(__APPLE__)
|
||||
AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
|
||||
#elif defined(ANDROID)
|
||||
AV_HWDEVICE_TYPE_MEDIACODEC,
|
||||
#elif defined(__unix__)
|
||||
AV_HWDEVICE_TYPE_CUDA,
|
||||
AV_HWDEVICE_TYPE_VAAPI,
|
||||
AV_HWDEVICE_TYPE_VDPAU,
|
||||
#endif
|
||||
AV_HWDEVICE_TYPE_VULKAN,
|
||||
};
|
||||
|
||||
AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) {
|
||||
const auto desc = av_pix_fmt_desc_get(codec_context->pix_fmt);
|
||||
if (desc && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
|
||||
for (int i = 0;; i++) {
|
||||
const AVCodecHWConfig* config = avcodec_get_hw_config(codec_context->codec, i);
|
||||
if (!config) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (const auto type : PreferredGpuDecoders) {
|
||||
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
|
||||
codec_context->pix_fmt = config->pix_fmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
|
||||
if (*p == codec_context->pix_fmt) {
|
||||
return codec_context->pix_fmt;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(HW_GPU, "Could not find supported GPU pixel format, falling back to CPU decoder");
|
||||
av_buffer_unref(&codec_context->hw_device_ctx);
|
||||
codec_context->pix_fmt = PreferredCpuFormat;
|
||||
return codec_context->pix_fmt;
|
||||
}
|
||||
|
||||
std::string AVError(int errnum) {
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {};
|
||||
av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum);
|
||||
return errbuf;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Packet::Packet(std::span<const u8> data) {
|
||||
m_packet = av_packet_alloc();
|
||||
m_packet->data = const_cast<u8*>(data.data());
|
||||
m_packet->size = static_cast<s32>(data.size());
|
||||
}
|
||||
|
||||
Packet::~Packet() {
|
||||
av_packet_free(&m_packet);
|
||||
}
|
||||
|
||||
Frame::Frame() {
|
||||
m_frame = av_frame_alloc();
|
||||
}
|
||||
|
||||
Frame::~Frame() {
|
||||
av_frame_free(&m_frame);
|
||||
}
|
||||
|
||||
Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||
const AVCodecID av_codec = [&] {
|
||||
switch (codec) {
|
||||
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
|
||||
return AV_CODEC_ID_H264;
|
||||
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
|
||||
return AV_CODEC_ID_VP8;
|
||||
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
|
||||
return AV_CODEC_ID_VP9;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown codec {}", codec);
|
||||
return AV_CODEC_ID_NONE;
|
||||
}
|
||||
}();
|
||||
m_codec = avcodec_find_decoder(av_codec);
|
||||
}
|
||||
|
||||
bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
|
||||
for (int i = 0;; i++) {
|
||||
const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i);
|
||||
if (!config) {
|
||||
LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name, av_hwdevice_get_type_name(type));
|
||||
break;
|
||||
}
|
||||
|
||||
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
|
||||
LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
|
||||
*out_pix_fmt = config->pix_fmt;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<AVHWDeviceType> HardwareContext::GetSupportedDeviceTypes() {
|
||||
std::vector<AVHWDeviceType> types;
|
||||
AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
|
||||
|
||||
while (true) {
|
||||
current_device_type = av_hwdevice_iterate_types(current_device_type);
|
||||
if (current_device_type == AV_HWDEVICE_TYPE_NONE) {
|
||||
return types;
|
||||
}
|
||||
|
||||
types.push_back(current_device_type);
|
||||
}
|
||||
}
|
||||
|
||||
HardwareContext::~HardwareContext() {
|
||||
av_buffer_unref(&m_gpu_decoder);
|
||||
}
|
||||
|
||||
bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder) {
|
||||
const auto supported_types = GetSupportedDeviceTypes();
|
||||
for (const auto type : PreferredGpuDecoders) {
|
||||
AVPixelFormat hw_pix_fmt;
|
||||
|
||||
if (std::ranges::find(supported_types, type) == supported_types.end()) {
|
||||
LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this->InitializeWithType(type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
|
||||
decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
|
||||
av_buffer_unref(&m_gpu_decoder);
|
||||
|
||||
if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0); ret < 0) {
|
||||
LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), AVError(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef LIBVA_FOUND
|
||||
if (type == AV_HWDEVICE_TYPE_VAAPI) {
|
||||
// We need to determine if this is an impersonated VAAPI driver.
|
||||
auto* hwctx = reinterpret_cast<AVHWDeviceContext*>(m_gpu_decoder->data);
|
||||
auto* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
|
||||
const char* vendor_name = vaQueryVendorString(vactx->display);
|
||||
if (strstr(vendor_name, "VDPAU backend")) {
|
||||
// VDPAU impersonated VAAPI impls are super buggy, we need to skip them.
|
||||
LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver");
|
||||
return false;
|
||||
} else {
|
||||
// According to some user testing, certain VAAPI drivers (Intel?) could be buggy.
|
||||
// Log the driver name just in case.
|
||||
LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DecoderContext::DecoderContext(const Decoder& decoder) : m_decoder{decoder} {
|
||||
m_codec_context = avcodec_alloc_context3(m_decoder.GetCodec());
|
||||
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
|
||||
m_codec_context->thread_count = 0;
|
||||
m_codec_context->thread_type &= ~FF_THREAD_FRAME;
|
||||
}
|
||||
|
||||
DecoderContext::~DecoderContext() {
|
||||
av_buffer_unref(&m_codec_context->hw_device_ctx);
|
||||
avcodec_free_context(&m_codec_context);
|
||||
}
|
||||
|
||||
void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt) {
|
||||
m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef());
|
||||
m_codec_context->get_format = GetGpuFormat;
|
||||
m_codec_context->pix_fmt = hw_pix_fmt;
|
||||
}
|
||||
|
||||
bool DecoderContext::OpenContext(const Decoder& decoder) {
|
||||
if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) {
|
||||
LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_codec_context->hw_device_ctx) {
|
||||
LOG_INFO(HW_GPU, "Using FFmpeg CPU decoder");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecoderContext::SendPacket(const Packet& packet) {
|
||||
if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) {
|
||||
LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
|
||||
auto ReceiveImpl = [&](AVFrame* frame) -> int {
|
||||
const int ret = avcodec_receive_frame(m_codec_context, frame);
|
||||
if (ret < 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) {
|
||||
LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
std::shared_ptr<Frame> intermediate_frame = std::make_shared<Frame>();
|
||||
if (ReceiveImpl(intermediate_frame->GetFrame()) < 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
m_final_frame = std::make_shared<Frame>();
|
||||
if (m_codec_context->hw_device_ctx) {
|
||||
m_final_frame->SetFormat(PreferredGpuFormat);
|
||||
if (const int ret = av_hwframe_transfer_data(m_final_frame->GetFrame(), intermediate_frame->GetFrame(), 0); ret < 0) {
|
||||
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
m_final_frame = std::move(intermediate_frame);
|
||||
}
|
||||
|
||||
return std::move(m_final_frame);
|
||||
}
|
||||
|
||||
void DecodeApi::Reset() {
|
||||
m_hardware_context.reset();
|
||||
m_decoder_context.reset();
|
||||
m_decoder.reset();
|
||||
}
|
||||
|
||||
bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||
this->Reset();
|
||||
m_decoder.emplace(codec);
|
||||
m_decoder_context.emplace(*m_decoder);
|
||||
|
||||
// Enable GPU decoding if requested.
|
||||
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
|
||||
m_hardware_context.emplace();
|
||||
m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
|
||||
}
|
||||
|
||||
// Open the decoder context.
|
||||
if (!m_decoder_context->OpenContext(*m_decoder)) {
|
||||
this->Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecodeApi::SendPacket(std::span<const u8> packet_data) {
|
||||
FFmpeg::Packet packet(packet_data);
|
||||
return m_decoder_context->SendPacket(packet);
|
||||
}
|
||||
|
||||
std::shared_ptr<Frame> DecodeApi::ReceiveFrame() {
|
||||
// Receive raw frame from decoder.
|
||||
return m_decoder_context->ReceiveFrame();
|
||||
}
|
||||
|
||||
}
|
||||
225
src/video_core/host1x/ffmpeg.h
Normal file
225
src/video_core/host1x/ffmpeg.h
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/host1x/nvdec_common.h"
|
||||
|
||||
extern "C" {
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#endif
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavcodec/codec.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace Tegra {
|
||||
class MemoryManager;
|
||||
}
|
||||
|
||||
namespace FFmpeg {
|
||||
|
||||
class Packet;
|
||||
class Frame;
|
||||
class Decoder;
|
||||
class HardwareContext;
|
||||
class DecoderContext;
|
||||
class DeinterlaceFilter;
|
||||
|
||||
// Wraps an AVPacket, a container for compressed bitstream data.
|
||||
class Packet {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Packet);
|
||||
YUZU_NON_MOVEABLE(Packet);
|
||||
|
||||
explicit Packet(std::span<const u8> data);
|
||||
~Packet();
|
||||
|
||||
AVPacket* GetPacket() const {
|
||||
return m_packet;
|
||||
}
|
||||
|
||||
private:
|
||||
AVPacket* m_packet{};
|
||||
};
|
||||
|
||||
// Wraps an AVFrame, a container for audio and video stream data.
|
||||
class Frame {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Frame);
|
||||
YUZU_NON_MOVEABLE(Frame);
|
||||
|
||||
explicit Frame();
|
||||
~Frame();
|
||||
|
||||
int GetWidth() const {
|
||||
return m_frame->width;
|
||||
}
|
||||
|
||||
int GetHeight() const {
|
||||
return m_frame->height;
|
||||
}
|
||||
|
||||
AVPixelFormat GetPixelFormat() const {
|
||||
return static_cast<AVPixelFormat>(m_frame->format);
|
||||
}
|
||||
|
||||
int GetStride(int plane) const {
|
||||
return m_frame->linesize[plane];
|
||||
}
|
||||
|
||||
int* GetStrides() const {
|
||||
return m_frame->linesize;
|
||||
}
|
||||
|
||||
u8* GetData(int plane) const {
|
||||
return m_frame->data[plane];
|
||||
}
|
||||
|
||||
const u8* GetPlane(int plane) const {
|
||||
return m_frame->data[plane];
|
||||
}
|
||||
|
||||
u8** GetPlanes() const {
|
||||
return m_frame->data;
|
||||
}
|
||||
|
||||
void SetFormat(int format) {
|
||||
m_frame->format = format;
|
||||
}
|
||||
|
||||
bool IsInterlaced() const {
|
||||
#if defined(FF_API_INTERLACED_FRAME) || LIBAVUTIL_VERSION_MAJOR >= 59
|
||||
return m_frame->flags & AV_FRAME_FLAG_INTERLACED;
|
||||
#else
|
||||
return m_frame->interlaced_frame;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsHardwareDecoded() const {
|
||||
return m_frame->hw_frames_ctx != nullptr;
|
||||
}
|
||||
|
||||
AVFrame* GetFrame() const {
|
||||
return m_frame;
|
||||
}
|
||||
|
||||
private:
|
||||
AVFrame* m_frame{};
|
||||
};
|
||||
|
||||
// Wraps an AVCodec, a type containing information about a codec.
|
||||
class Decoder {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Decoder);
|
||||
YUZU_NON_MOVEABLE(Decoder);
|
||||
|
||||
explicit Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec);
|
||||
~Decoder() = default;
|
||||
|
||||
bool SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const;
|
||||
|
||||
const AVCodec* GetCodec() const {
|
||||
return m_codec;
|
||||
}
|
||||
|
||||
private:
|
||||
const AVCodec* m_codec{};
|
||||
};
|
||||
|
||||
// Wraps AVBufferRef for an accelerated decoder.
|
||||
class HardwareContext {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(HardwareContext);
|
||||
YUZU_NON_MOVEABLE(HardwareContext);
|
||||
|
||||
static std::vector<AVHWDeviceType> GetSupportedDeviceTypes();
|
||||
|
||||
explicit HardwareContext() = default;
|
||||
~HardwareContext();
|
||||
|
||||
bool InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder);
|
||||
|
||||
AVBufferRef* GetBufferRef() const {
|
||||
return m_gpu_decoder;
|
||||
}
|
||||
|
||||
private:
|
||||
bool InitializeWithType(AVHWDeviceType type);
|
||||
|
||||
AVBufferRef* m_gpu_decoder{};
|
||||
};
|
||||
|
||||
// Wraps an AVCodecContext.
|
||||
class DecoderContext {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(DecoderContext);
|
||||
YUZU_NON_MOVEABLE(DecoderContext);
|
||||
|
||||
explicit DecoderContext(const Decoder& decoder);
|
||||
~DecoderContext();
|
||||
|
||||
void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
|
||||
bool OpenContext(const Decoder& decoder);
|
||||
bool SendPacket(const Packet& packet);
|
||||
std::shared_ptr<Frame> ReceiveFrame();
|
||||
|
||||
AVCodecContext* GetCodecContext() const {
|
||||
return m_codec_context;
|
||||
}
|
||||
|
||||
bool UsingDecodeOrder() const {
|
||||
return m_decode_order;
|
||||
}
|
||||
|
||||
private:
|
||||
const Decoder& m_decoder;
|
||||
AVCodecContext* m_codec_context{};
|
||||
std::shared_ptr<Frame> m_final_frame{};
|
||||
bool m_decode_order{};
|
||||
};
|
||||
|
||||
class DecodeApi {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(DecodeApi);
|
||||
YUZU_NON_MOVEABLE(DecodeApi);
|
||||
|
||||
DecodeApi() = default;
|
||||
~DecodeApi() = default;
|
||||
|
||||
bool Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec);
|
||||
void Reset();
|
||||
|
||||
bool UsingDecodeOrder() const {
|
||||
return m_decoder_context->UsingDecodeOrder();
|
||||
}
|
||||
|
||||
bool SendPacket(std::span<const u8> packet_data);
|
||||
std::shared_ptr<Frame> ReceiveFrame();
|
||||
|
||||
private:
|
||||
std::optional<FFmpeg::Decoder> m_decoder;
|
||||
std::optional<FFmpeg::DecoderContext> m_decoder_context;
|
||||
std::optional<FFmpeg::HardwareContext> m_hardware_context;
|
||||
};
|
||||
|
||||
} // namespace FFmpeg
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
|
|
@ -12,9 +12,12 @@
|
|||
namespace Tegra::Host1x {
|
||||
|
||||
Host1x::Host1x(Core::System& system_)
|
||||
: system{system_}, syncpoint_manager{},
|
||||
memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 0, 12},
|
||||
allocator{std::make_unique<Common::FlatAllocator<u32, 0, 32>>(1 << 12)} {}
|
||||
: system{system_}
|
||||
, syncpoint_manager{}
|
||||
, memory_manager(system.DeviceMemory())
|
||||
, gmmu_manager{system, memory_manager, 32, 0, 12}
|
||||
, allocator{1 << 12}
|
||||
{}
|
||||
|
||||
Host1x::~Host1x() = default;
|
||||
|
||||
|
|
@ -24,13 +27,13 @@ void Host1x::StartDevice(s32 fd, ChannelType type, u32 syncpt) {
|
|||
#ifdef YUZU_LEGACY
|
||||
std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
|
||||
#endif
|
||||
devices[fd] = std::make_unique<Tegra::Host1x::Nvdec>(*this, fd, syncpt, frame_queue);
|
||||
devices[fd] = std::make_unique<Tegra::Host1x::Nvdec>(*this, fd, syncpt);
|
||||
break;
|
||||
case ChannelType::VIC:
|
||||
#ifdef YUZU_LEGACY
|
||||
std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
|
||||
#endif
|
||||
devices[fd] = std::make_unique<Tegra::Host1x::Vic>(*this, fd, syncpt, frame_queue);
|
||||
devices[fd] = std::make_unique<Tegra::Host1x::Vic>(*this, fd, syncpt);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", static_cast<u32>(type));
|
||||
|
|
|
|||
|
|
@ -184,20 +184,12 @@ public:
|
|||
return memory_manager;
|
||||
}
|
||||
|
||||
Tegra::MemoryManager& GMMU() {
|
||||
return gmmu_manager;
|
||||
}
|
||||
|
||||
const Tegra::MemoryManager& GMMU() const {
|
||||
return gmmu_manager;
|
||||
}
|
||||
|
||||
Common::FlatAllocator<u32, 0, 32>& Allocator() {
|
||||
return *allocator;
|
||||
return allocator;
|
||||
}
|
||||
|
||||
const Common::FlatAllocator<u32, 0, 32>& Allocator() const {
|
||||
return *allocator;
|
||||
return allocator;
|
||||
}
|
||||
|
||||
void StartDevice(s32 fd, ChannelType type, u32 syncpt);
|
||||
|
|
@ -211,12 +203,11 @@ public:
|
|||
it->second->PushEntries(std::move(entries));
|
||||
}
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
SyncpointManager syncpoint_manager;
|
||||
Tegra::MaxwellDeviceMemoryManager memory_manager;
|
||||
Tegra::MemoryManager gmmu_manager;
|
||||
std::unique_ptr<Common::FlatAllocator<u32, 0, 32>> allocator;
|
||||
Common::FlatAllocator<u32, 0, 32> allocator;
|
||||
FrameQueue frame_queue;
|
||||
ankerl::unordered_dense::map<s32, std::unique_ptr<CDmaPusher>> devices;
|
||||
#ifdef YUZU_LEGACY
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue