code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>

#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "video_core/cdma_pusher.h"
#include "video_core/framebuffer_config.h"
#include "video_core/rasterizer_download_area.h"

namespace Core {
class System;
} // namespace Core

namespace VideoCore {
class RendererBase;
class ShaderNotify;
} // namespace VideoCore

namespace Tegra {
class DmaPusher;
struct CommandList;

// TODO: Implement the commented ones
enum class RenderTargetFormat : u32 {
    NONE = 0x0,
    R32G32B32A32_FLOAT = 0xC0,
    R32G32B32A32_SINT = 0xC1,
    R32G32B32A32_UINT = 0xC2,
    R32G32B32X32_FLOAT = 0xC3,
    R32G32B32X32_SINT = 0xC4,
    R32G32B32X32_UINT = 0xC5,
    R16G16B16A16_UNORM = 0xC6,
    R16G16B16A16_SNORM = 0xC7,
    R16G16B16A16_SINT = 0xC8,
    R16G16B16A16_UINT = 0xC9,
    R16G16B16A16_FLOAT = 0xCA,
    R32G32_FLOAT = 0xCB,
    R32G32_SINT = 0xCC,
    R32G32_UINT = 0xCD,
    R16G16B16X16_FLOAT = 0xCE,
    A8R8G8B8_UNORM = 0xCF,
    A8R8G8B8_SRGB = 0xD0,
    A2B10G10R10_UNORM = 0xD1,
    A2B10G10R10_UINT = 0xD2,
    A8B8G8R8_UNORM = 0xD5,
    A8B8G8R8_SRGB = 0xD6,
    A8B8G8R8_SNORM = 0xD7,
    A8B8G8R8_SINT = 0xD8,
    A8B8G8R8_UINT = 0xD9,
    R16G16_UNORM = 0xDA,
    R16G16_SNORM = 0xDB,
    R16G16_SINT = 0xDC,
    R16G16_UINT = 0xDD,
    R16G16_FLOAT = 0xDE,
    A2R10G10B10_UNORM = 0xDF,
    B10G11R11_FLOAT = 0xE0,
    R32_SINT = 0xE3,
    R32_UINT = 0xE4,
    R32_FLOAT = 0xE5,
    X8R8G8B8_UNORM = 0xE6,
    X8R8G8B8_SRGB = 0xE7,
    R5G6B5_UNORM = 0xE8,
    A1R5G5B5_UNORM = 0xE9,
    R8G8_UNORM = 0xEA,
    R8G8_SNORM = 0xEB,
    R8G8_SINT = 0xEC,
    R8G8_UINT = 0xED,
    R16_UNORM = 0xEE,
    R16_SNORM = 0xEF,
    R16_SINT = 0xF0,
    R16_UINT = 0xF1,
    R16_FLOAT = 0xF2,
    R8_UNORM = 0xF3,
    R8_SNORM = 0xF4,
    R8_SINT = 0xF5,
    R8_UINT = 0xF6,

    // A8_UNORM = 0xF7,
    X1R5G5B5_UNORM = 0xF8,
    X8B8G8R8_UNORM = 0xF9,
    X8B8G8R8_SRGB = 0xFA,
    /*
    Z1R5G5B5_UNORM = 0xFB,
    O1R5G5B5_UNORM = 0xFC,
    Z8R8G8B8_UNORM = 0xFD,
    O8R8G8B8_UNORM = 0xFE,
    R32_UNORM = 0xFF,
    A16_UNORM = 0x40,
    A16_FLOAT = 0x41,
    A32_FLOAT = 0x42,
    A8R8_UNORM = 0x43,
    R16A16_UNORM = 0x44,
    R16A16_FLOAT = 0x45,
    R32A32_FLOAT = 0x46,
    B8G8R8A8_UNORM = 0x47,
    */
};

enum class DepthFormat : u32 {
    Z32_FLOAT = 0xA,
    Z16_UNORM = 0x13,
    Z24_UNORM_S8_UINT = 0x14,
    X8Z24_UNORM = 0x15,
    S8Z24_UNORM = 0x16,
    S8_UINT = 0x17,
    V8Z24_UNORM = 0x18,
    Z32_FLOAT_X24S8_UINT = 0x19,
    /*
    X8Z24_UNORM_X16V8S8_UINT = 0x1D,
    Z32_FLOAT_X16V8X8_UINT = 0x1E,
    Z32_FLOAT_X16V8S8_UINT = 0x1F,
    */
};

namespace Engines {
class Maxwell3D;
class KeplerCompute;
} // namespace Engines

namespace Control {
struct ChannelState;
}

namespace Host1x {
class Host1x;
} // namespace Host1x

class MemoryManager;

class GPU final {
public:
    explicit GPU(Core::System& system, bool is_async, bool use_nvdec);
    ~GPU();

    /// Binds a renderer to the GPU.
    void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer);

    /// Flush all current written commands into the host GPU for execution.
    void FlushCommands();
    /// Synchronizes CPU writes with Host GPU memory.
    void InvalidateGPUCache();
    /// Signal the ending of command list.
    void OnCommandListEnd();

    std::shared_ptr<Control::ChannelState> AllocateChannel();

    void InitChannel(Control::ChannelState& to_init, u64 program_id);

    void BindChannel(s32 channel_id);

    void ReleaseChannel(Control::ChannelState& to_release);

    void InitAddressSpace(Tegra::MemoryManager& memory_manager);

    /// Request a host GPU memory flush from the CPU.
    [[nodiscard]] u64 RequestFlush(DAddr addr, std::size_t size);

    /// Obtains current flush request fence id.
    [[nodiscard]] u64 CurrentSyncRequestFence() const;

    void WaitForSyncOperation(u64 fence);

    /// Tick pending requests within the GPU.
    void TickWork();

    /// Gets a mutable reference to the Host1x interface
    [[nodiscard]] Host1x::Host1x& Host1x();

    /// Gets an immutable reference to the Host1x interface.
    [[nodiscard]] const Host1x::Host1x& Host1x() const;

    /// Returns a reference to the Maxwell3D GPU engine.
    [[nodiscard]] Engines::Maxwell3D& Maxwell3D();

    /// Returns a const reference to the Maxwell3D GPU engine.
    [[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const;

    /// Returns a reference to the KeplerCompute GPU engine.
    [[nodiscard]] Engines::KeplerCompute& KeplerCompute();

    /// Returns a reference to the KeplerCompute GPU engine.
    [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const;

    /// Returns a reference to the GPU DMA pusher.
    [[nodiscard]] Tegra::DmaPusher& DmaPusher();

    /// Returns a const reference to the GPU DMA pusher.
    [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const;

    /// Returns a reference to the underlying renderer.
    [[nodiscard]] VideoCore::RendererBase& Renderer();

    /// Returns a const reference to the underlying renderer.
    [[nodiscard]] const VideoCore::RendererBase& Renderer() const;

    /// Returns a reference to the shader notifier.
    [[nodiscard]] VideoCore::ShaderNotify& ShaderNotify();

    /// Returns a const reference to the shader notifier.
    [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const;

    [[nodiscard]] u64 GetTicks() const;

    [[nodiscard]] bool IsAsync() const;

    [[nodiscard]] bool UseNvdec() const;

    void RendererFrameEndNotify();

    void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
                          std::vector<Service::Nvidia::NvFence>&& fences);

    std::vector<u8> GetAppletCaptureBuffer();

    /// Performs any additional setup necessary in order to begin GPU emulation.
    /// This can be used to launch any necessary threads and register any necessary
    /// core timing events.
    void Start();

    /// Performs any additional necessary steps to shutdown GPU emulation.
    void NotifyShutdown();

    /// Obtain the CPU Context
    void ObtainContext();

    /// Release the CPU Context
    void ReleaseContext();

    /// Push GPU command entries to be processed
    void PushGPUEntries(s32 channel, Tegra::CommandList&& entries);

    /// Push GPU command buffer entries to be processed
    void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries);

    /// Frees the CDMAPusher instance to free up resources
    void ClearCdmaInstance(u32 id);

    /// Swap buffers (render frame)
    void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);

    /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
    [[nodiscard]] VideoCore::RasterizerDownloadArea OnCPURead(DAddr addr, u64 size);

    /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
    void FlushRegion(DAddr addr, u64 size);

    /// Notify rasterizer that any caches of the specified region should be invalidated
    void InvalidateRegion(DAddr addr, u64 size);

    /// Notify rasterizer that CPU is trying to write this area. It returns true if the area is
    /// sensible, false otherwise
    bool OnCPUWrite(DAddr addr, u64 size);

    /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
    void FlushAndInvalidateRegion(DAddr addr, u64 size);

private:
    struct Impl;
    mutable std::unique_ptr<Impl> impl;
};

} // namespace Tegra


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/assert.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "common/thread.h"
#include "core/core.h"
#include "core/frontend/graphics_context.h"
#include "video_core/control/scheduler.h"
#include "video_core/dma_pusher.h"
#include "video_core/gpu.h"
#include "video_core/gpu_thread.h"
#include "video_core/renderer_base.h"

namespace VideoCommon::GPUThread {

/// Runs the GPU thread
static void RunThread(std::stop_token stop_token, Core::System& system,
                      VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
                      Tegra::Control::Scheduler& scheduler, SynchState& state) {
    std::string name = "GPU";
    MicroProfileOnThreadCreate(name.c_str());
    SCOPE_EXIT {
        MicroProfileOnThreadExit();
    };

    Common::SetCurrentThreadName(name.c_str());
    Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
    system.RegisterHostThread();

    auto current_context = context.Acquire();
    VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();

    CommandDataContainer next;

    while (!stop_token.stop_requested()) {
        state.queue.PopWait(next, stop_token);
        if (stop_token.stop_requested()) {
            break;
        }
        if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
            scheduler.Push(submit_list->channel, std::move(submit_list->entries));
        } else if (std::holds_alternative<GPUTickCommand>(next.data)) {
            system.GPU().TickWork();
        } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
            rasterizer->FlushRegion(flush->addr, flush->size);
        } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) {
            rasterizer->OnCacheInvalidation(invalidate->addr, invalidate->size);
        } else {
            ASSERT(false);
        }
        state.signaled_fence.store(next.fence);
        if (next.block) {
            // We have to lock the write_lock to ensure that the condition_variable wait not get a
            // race between the check and the lock itself.
            std::scoped_lock lk{state.write_lock};
            state.cv.notify_all();
        }
    }
}

ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
    : system{system_}, is_async{is_async_} {}

ThreadManager::~ThreadManager() = default;

void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
                                Core::Frontend::GraphicsContext& context,
                                Tegra::Control::Scheduler& scheduler) {
    rasterizer = renderer.ReadRasterizer();
    thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
                          std::ref(scheduler), std::ref(state));
}

void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) {
    PushCommand(SubmitListCommand(channel, std::move(entries)));
}

void ThreadManager::FlushRegion(DAddr addr, u64 size) {
    if (!is_async) {
        // Always flush with synchronous GPU mode
        PushCommand(FlushRegionCommand(addr, size));
        return;
    }
    if (!Settings::IsGPULevelExtreme()) {
        return;
    }
    auto& gpu = system.GPU();
    u64 fence = gpu.RequestFlush(addr, size);
    TickGPU();
    gpu.WaitForSyncOperation(fence);
}

void ThreadManager::TickGPU() {
    PushCommand(GPUTickCommand());
}

void ThreadManager::InvalidateRegion(DAddr addr, u64 size) {
    rasterizer->OnCacheInvalidation(addr, size);
}

void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) {
    // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important
    rasterizer->OnCacheInvalidation(addr, size);
}

u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
    if (!is_async) {
        // In synchronous GPU mode, block the caller until the command has executed
        block = true;
    }

    std::unique_lock lk(state.write_lock);
    const u64 fence{++state.last_fence};
    state.queue.EmplaceWait(std::move(command_data), fence, block);

    if (block) {
        Common::CondvarWait(state.cv, lk, thread.get_stop_token(), [this, fence] {
            return fence <= state.signaled_fence.load(std::memory_order_relaxed);
        });
    }

    return fence;
}

} // namespace VideoCommon::GPUThread

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <atomic>
#include <condition_variable>
#include <mutex>
#include <optional>
#include <thread>
#include <variant>

#include "common/bounded_threadsafe_queue.h"
#include "common/polyfill_thread.h"
#include "video_core/framebuffer_config.h"

namespace Tegra {
struct FramebufferConfig;
namespace Control {
class Scheduler;
}
} // namespace Tegra

namespace Core {
namespace Frontend {
class GraphicsContext;
}
class System;
} // namespace Core

namespace VideoCore {
class RasterizerInterface;
class RendererBase;
} // namespace VideoCore

namespace VideoCommon::GPUThread {

/// Command to signal to the GPU thread that a command list is ready for processing
struct SubmitListCommand final {
    explicit SubmitListCommand(s32 channel_, Tegra::CommandList&& entries_)
        : channel{channel_}, entries{std::move(entries_)} {}

    s32 channel;
    Tegra::CommandList entries;
};

/// Command to signal to the GPU thread to flush a region
struct FlushRegionCommand final {
    explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {}

    DAddr addr;
    u64 size;
};

/// Command to signal to the GPU thread to invalidate a region
struct InvalidateRegionCommand final {
    explicit constexpr InvalidateRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {}

    DAddr addr;
    u64 size;
};

/// Command to signal to the GPU thread to flush and invalidate a region
struct FlushAndInvalidateRegionCommand final {
    explicit constexpr FlushAndInvalidateRegionCommand(DAddr addr_, u64 size_)
        : addr{addr_}, size{size_} {}

    DAddr addr;
    u64 size;
};

/// Command to make the gpu look into pending requests
struct GPUTickCommand final {};

using CommandData =
    std::variant<std::monostate, SubmitListCommand, FlushRegionCommand, InvalidateRegionCommand,
                 FlushAndInvalidateRegionCommand, GPUTickCommand>;

struct CommandDataContainer {
    CommandDataContainer() = default;

    explicit CommandDataContainer(CommandData&& data_, u64 next_fence_, bool block_)
        : data{std::move(data_)}, fence{next_fence_}, block(block_) {}

    CommandData data;
    u64 fence{};
    bool block{};
};

/// Struct used to synchronize the GPU thread
struct SynchState final {
    using CommandQueue = Common::MPSCQueue<CommandDataContainer>;
    std::mutex write_lock;
    CommandQueue queue;
    u64 last_fence{};
    std::atomic<u64> signaled_fence{};
    std::condition_variable_any cv;
};

/// Class used to manage the GPU thread
class ThreadManager final {
public:
    explicit ThreadManager(Core::System& system_, bool is_async_);
    ~ThreadManager();

    /// Creates and starts the GPU thread.
    void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
                     Tegra::Control::Scheduler& scheduler);

    /// Push GPU command entries to be processed
    void SubmitList(s32 channel, Tegra::CommandList&& entries);

    /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
    void FlushRegion(DAddr addr, u64 size);

    /// Notify rasterizer that any caches of the specified region should be invalidated
    void InvalidateRegion(DAddr addr, u64 size);

    /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
    void FlushAndInvalidateRegion(DAddr addr, u64 size);

    void TickGPU();

private:
    /// Pushes a command to be executed by the GPU thread
    u64 PushCommand(CommandData&& command_data, bool block = false);

    Core::System& system;
    const bool is_async;
    VideoCore::RasterizerInterface* rasterizer = nullptr;

    SynchState state;
    std::jthread thread;
};

} // namespace VideoCommon::GPUThread


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <iterator>
#include <memory>
#include <optional>
#include <span>
#include <vector>

#include "common/scratch_buffer.h"
#include "core/guest_memory.h"
#include "video_core/memory_manager.h"

namespace Tegra::Memory {

using GuestMemoryFlags = Core::Memory::GuestMemoryFlags;

template <typename T, GuestMemoryFlags FLAGS>
using DeviceGuestMemory = Core::Memory::GuestMemory<Tegra::MaxwellDeviceMemoryManager, T, FLAGS>;
template <typename T, GuestMemoryFlags FLAGS>
using DeviceGuestMemoryScoped =
    Core::Memory::GuestMemoryScoped<Tegra::MaxwellDeviceMemoryManager, T, FLAGS>;
template <typename T, GuestMemoryFlags FLAGS>
using GpuGuestMemory = Core::Memory::GuestMemory<Tegra::MemoryManager, T, FLAGS>;
template <typename T, GuestMemoryFlags FLAGS>
using GpuGuestMemoryScoped = Core::Memory::GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>;

} // namespace Tegra::Memory


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/assert.h"
#include "common/settings.h"
#include "video_core/host1x/codecs/codec.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/host1x.h"
#include "video_core/memory_manager.h"

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)) {}

Codec::~Codec() = default;

void Codec::Initialize() {
    initialized = decode_api.Initialize(current_codec);
}

void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
    if (current_codec != codec) {
        current_codec = codec;
        LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName());
    }
}

void Codec::Decode() {
    const bool is_first_frame = !initialized;
    if (is_first_frame) {
        Initialize();
    }

    if (!initialized) {
        return;
    }

    // Assemble bitstream.
    bool vp9_hidden_frame = false;
    size_t configuration_size = 0;
    const auto packet_data = [&]() {
        switch (current_codec) {
        case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
            return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame);
        case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
            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();
        default:
            ASSERT(false);
            return std::span<const u8>{};
        }
    }();

    // Send assembled bitstream to decoder.
    if (!decode_api.SendPacket(packet_data, configuration_size)) {
        return;
    }

    // Only receive/store visible frames.
    if (vp9_hidden_frame) {
        return;
    }

    // Receive output frames from decoder.
    decode_api.ReceiveFrames(frames);

    while (frames.size() > 10) {
        LOG_DEBUG(HW_GPU, "ReceiveFrames overflow, dropped frame");
        frames.pop();
    }
}

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()) {
        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:
        return "None";
    case Host1x::NvdecCommon::VideoCodec::H264:
        return "H264";
    case Host1x::NvdecCommon::VideoCodec::VP8:
        return "VP8";
    case Host1x::NvdecCommon::VideoCodec::H265:
        return "H265";
    case Host1x::NvdecCommon::VideoCodec::VP9:
        return "VP9";
    default:
        return "Unknown";
    }
}
} // namespace Tegra


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include <optional>
#include <string_view>
#include <queue>
#include "common/common_types.h"
#include "video_core/host1x/ffmpeg/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

class Codec {
public:
    explicit Codec(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs);
    ~Codec();

    /// Initialize the codec, returning success or failure
    void Initialize();

    /// Sets NVDEC video stream codec
    void SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec);

    /// Call decoders to construct headers, decode AVFrame with ffmpeg
    void Decode();

    /// 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;

private:
    bool initialized{};
    Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None};
    FFmpeg::DecodeApi decode_api;

    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;

    std::queue<std::unique_ptr<FFmpeg::Frame>> frames{};
};

} // namespace Tegra


code:
// SPDX-FileCopyrightText: Ryujinx Team and Contributors
// SPDX-License-Identifier: MIT

#include <array>
#include <bit>

#include "common/scratch_buffer.h"
#include "common/settings.h"
#include "video_core/host1x/codecs/h264.h"
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"

namespace Tegra::Decoder {
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_) : host1x{host1x_} {}

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));

    const s64 frame_number = 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;
    }

    // Encode header
    H264BitWriter writer{};
    writer.WriteU(1, 24);
    writer.WriteU(0, 1);
    writer.WriteU(3, 2);
    writer.WriteU(7, 5);
    writer.WriteU(100, 8);
    writer.WriteU(0, 8);
    writer.WriteU(31, 8);
    writer.WriteUe(0);
    const u32 chroma_format_idc =
        static_cast<u32>(context.h264_parameter_set.chroma_format_idc.Value());
    writer.WriteUe(chroma_format_idc);
    if (chroma_format_idc == 3) {
        writer.WriteBit(false);
    }

    writer.WriteUe(0);
    writer.WriteUe(0);
    writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
    writer.WriteBit(false); // Scaling matrix present flag

    writer.WriteUe(static_cast<u32>(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());
    writer.WriteUe(order_cnt_type);
    if (order_cnt_type == 0) {
        writer.WriteUe(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.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);

    // 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;
    writer.WriteUe(max_num_ref_frames);
    writer.WriteBit(false);
    writer.WriteUe(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);

    if (!context.h264_parameter_set.frame_mbs_only_flag) {
        writer.WriteBit(context.h264_parameter_set.flags.mbaff_frame.Value() != 0);
    }

    writer.WriteBit(context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0);
    writer.WriteBit(false); // Frame cropping flag
    writer.WriteBit(false); // VUI parameter present flag

    writer.End();

    // H264 PPS
    writer.WriteU(1, 24);
    writer.WriteU(0, 1);
    writer.WriteU(3, 2);
    writer.WriteU(8, 5);

    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.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.WriteSe(pic_init_qp);
    writer.WriteSe(0);
    s32 chroma_qp_index_offset =
        static_cast<s32>(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(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);
    }

    if (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);
        }
    }

    s32 chroma_qp_index_offset2 =
        static_cast<s32>(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());

    *out_configuration_size = encoded_header.size();
    host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data() + encoded_header.size(),
                            context.stream_len);

    return frame;
}

H264BitWriter::H264BitWriter() = default;

H264BitWriter::~H264BitWriter() = default;

void H264BitWriter::WriteU(s32 value, s32 value_sz) {
    WriteBits(value, value_sz);
}

void H264BitWriter::WriteSe(s32 value) {
    WriteExpGolombCodedInt(value);
}

void H264BitWriter::WriteUe(u32 value) {
    WriteExpGolombCodedUInt(value);
}

void H264BitWriter::End() {
    WriteBit(true);
    Flush();
}

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);
    if (count == 16) {
        std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());
    } 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;
    }
}

std::vector<u8>& H264BitWriter::GetByteArray() {
    return byte_array;
}

const std::vector<u8>& H264BitWriter::GetByteArray() const {
    return byte_array;
}

void H264BitWriter::WriteBits(s32 value, s32 bit_count) {
    s32 value_pos = 0;

    s32 remaining = bit_count;

    while (remaining > 0) {
        s32 copy_size = remaining;

        const s32 free_bits = GetFreeBufferBits();

        if (copy_size > free_bits) {
            copy_size = free_bits;
        }

        const s32 mask = (1 << copy_size) - 1;

        const s32 src_shift = (bit_count - value_pos) - copy_size;
        const s32 dst_shift = (buffer_size - buffer_pos) - copy_size;

        buffer |= ((value >> src_shift) & mask) << dst_shift;

        value_pos += copy_size;
        buffer_pos += copy_size;
        remaining -= copy_size;
    }
}

void H264BitWriter::WriteExpGolombCodedInt(s32 value) {
    const s32 sign = value <= 0 ? 0 : 1;
    if (value < 0) {
        value = -value;
    }
    value = (value << 1) - sign;
    WriteExpGolombCodedUInt(value);
}

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);
}

s32 H264BitWriter::GetFreeBufferBits() {
    if (buffer_pos == buffer_size) {
        Flush();
    }

    return buffer_size - buffer_pos;
}

void H264BitWriter::Flush() {
    if (buffer_pos == 0) {
        return;
    }
    byte_array.push_back(static_cast<u8>(buffer));

    buffer = 0;
    buffer_pos = 0;
}
} // namespace Tegra::Decoder


code:
// SPDX-FileCopyrightText: Ryujinx Team and Contributors
// SPDX-License-Identifier: MIT

#pragma once

#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/nvdec_common.h"

namespace Tegra {

namespace Host1x {
class Host1x;
} // namespace Host1x

namespace Decoder {

class H264BitWriter {
public:
    H264BitWriter();
    ~H264BitWriter();

    /// The following Write methods are based on clause 9.1 in the H.264 specification.
    /// WriteSe and WriteUe write in the Exp-Golomb-coded syntax
    void WriteU(s32 value, s32 value_sz);
    void WriteSe(s32 value);
    void WriteUe(u32 value);

    /// Finalize the bitstream
    void End();

    /// append a bit to the stream, equivalent value to the state parameter
    void WriteBit(bool state);

    /// 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);

    /// Return the bitstream as a vector.
    [[nodiscard]] std::vector<u8>& GetByteArray();
    [[nodiscard]] const std::vector<u8>& GetByteArray() const;

private:
    void WriteBits(s32 value, s32 bit_count);
    void WriteExpGolombCodedInt(s32 value);
    void WriteExpGolombCodedUInt(u32 value);
    [[nodiscard]] s32 GetFreeBufferBits();
    void Flush();

    s32 buffer_size{8};

    s32 buffer{};
    s32 buffer_pos{};
    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);

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;
        };
    };
    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
    };
    static_assert(sizeof(H264DecoderContext) == 0x2A0, "H264DecoderContext 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);
#undef ASSERT_POSITION

#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);
#undef ASSERT_POSITION
};

} // namespace Decoder
} // namespace Tegra


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <vector>

#include "video_core/host1x/codecs/vp8.h"
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"

namespace Tegra::Decoder {
VP8::VP8(Host1x::Host1x& host1x_) : host1x{host1x_} {}

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));

    const bool is_key_frame = info.key_frame == 1u;
    const auto bitstream_size = static_cast<size_t>(info.vld_buffer_size);
    const size_t header_size = is_key_frame ? 10u : 3u;
    frame.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

    // 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);

    if (is_key_frame) {
        frame[3] = 0x9du;
        frame[4] = 0x01u;
        frame[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));
        // 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));
    }
    const u64 bitstream_offset = state.frame_bitstream_offset;
    host1x.GMMU().ReadBlock(bitstream_offset, frame.data() + header_size, bitstream_size);

    return frame;
}

} // namespace Tegra::Decoder


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <span>

#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/scratch_buffer.h"
#include "video_core/host1x/nvdec_common.h"

namespace Tegra {

namespace Host1x {
class Host1x;
} // namespace Host1x

namespace Decoder {

class VP8 {
public:
    explicit VP8(Host1x::Host1x& host1x);
    ~VP8();

    /// Compose the VP8 frame for FFmpeg decoding
    [[nodiscard]] std::span<const u8> ComposeFrame(
        const Host1x::NvdecCommon::NvdecRegisters& state);

private:
    Common::ScratchBuffer<u8> frame;
    Host1x::Host1x& host1x;

    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 Decoder
} // namespace Tegra


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <span>
#include <vector>

#include "common/common_types.h"
#include "common/scratch_buffer.h"
#include "common/stream.h"
#include "video_core/host1x/codecs/vp9_types.h"
#include "video_core/host1x/nvdec_common.h"

namespace Tegra {

namespace Host1x {
class Host1x;
} // namespace Host1x

namespace Decoder {

/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the
/// VP9 header bitstreams.

class VpxRangeEncoder {
public:
    VpxRangeEncoder();
    ~VpxRangeEncoder();

    VpxRangeEncoder(const VpxRangeEncoder&) = delete;
    VpxRangeEncoder& operator=(const VpxRangeEncoder&) = delete;

    VpxRangeEncoder(VpxRangeEncoder&&) = default;
    VpxRangeEncoder& operator=(VpxRangeEncoder&&) = default;

    /// Writes the rightmost value_size bits from value into the stream
    void Write(s32 value, s32 value_size);

    /// Writes a single bit with half probability
    void Write(bool bit);

    /// Writes a bit to the base_stream encoded with probability
    void Write(bool bit, s32 probability);

    /// Signal the end of the bitstream
    void End();

    [[nodiscard]] std::vector<u8>& GetBuffer() {
        return base_stream.GetBuffer();
    }

    [[nodiscard]] const std::vector<u8>& GetBuffer() const {
        return base_stream.GetBuffer();
    }

private:
    u8 PeekByte();
    Common::Stream base_stream{};
    u32 low_value{};
    u32 range{0xff};
    s32 count{-24};
    s32 half_probability{128};
};

class VpxBitStreamWriter {
public:
    VpxBitStreamWriter();
    ~VpxBitStreamWriter();

    VpxBitStreamWriter(const VpxBitStreamWriter&) = delete;
    VpxBitStreamWriter& operator=(const VpxBitStreamWriter&) = delete;

    VpxBitStreamWriter(VpxBitStreamWriter&&) = default;
    VpxBitStreamWriter& operator=(VpxBitStreamWriter&&) = default;

    /// Write an unsigned integer value
    void WriteU(u32 value, u32 value_size);

    /// Write a signed integer value
    void WriteS(s32 value, u32 value_size);

    /// Based on 6.2.10 of VP9 Spec, writes a delta coded value
    void WriteDeltaQ(u32 value);

    /// Write a single bit.
    void WriteBit(bool state);

    /// Pushes current buffer into buffer_array, resets buffer
    void Flush();

    /// Returns byte_array
    [[nodiscard]] std::vector<u8>& GetByteArray();

    /// Returns const byte_array
    [[nodiscard]] const std::vector<u8>& GetByteArray() const;

private:
    /// Write bit_count bits from value into buffer
    void WriteBits(u32 value, u32 bit_count);

    /// Gets next available position in buffer, invokes Flush() if buffer is full
    s32 GetFreeBufferBits();

    s32 buffer_size{8};

    s32 buffer{};
    s32 buffer_pos{};
    std::vector<u8> byte_array;
};

class VP9 {
public:
    explicit VP9(Host1x::Host1x& host1x);
    ~VP9();

    VP9(const VP9&) = delete;
    VP9& operator=(const VP9&) = delete;

    VP9(VP9&&) = default;
    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);

    /// Returns true if the most recent frame was a hidden frame.
    [[nodiscard]] bool WasFrameHidden() const {
        return !current_frame_info.show_frame;
    }

    /// Returns a const span to the composed frame data.
    [[nodiscard]] std::span<const u8> GetFrameBytes() const {
        return frame;
    }

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,
                                const std::array<T, N>& old_prob);

    /// Generates compressed header probability updates in the bitstream writer
    /// If probs are not equal, WriteProbabilityDelta is invoked
    void WriteProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);

    /// Generates compressed header probability deltas in the bitstream writer
    void WriteProbabilityDelta(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);

    /// Inverse of 6.3.4 Decode term subexp
    void EncodeTermSubExp(VpxRangeEncoder& writer, s32 value);

    /// Writes if the value is less than the test value
    bool WriteLessThan(VpxRangeEncoder& writer, s32 value, s32 test);

    /// Writes probability updates for the Coef probabilities
    void WriteCoefProbabilityUpdate(VpxRangeEncoder& writer, s32 tx_mode,
                                    const std::array<u8, 1728>& new_prob,
                                    const std::array<u8, 1728>& old_prob);

    /// Write probabilities for 4-byte aligned structures
    template <typename T, std::size_t N>
    void WriteProbabilityUpdateAligned4(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
                                        const std::array<T, N>& old_prob);

    /// Write motion vector probability updates. 6.3.17 in the spec
    void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);

    /// Returns VP9 information from NVDEC provided offset and size
    [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo(
        const Host1x::NvdecCommon::NvdecRegisters& state);

    /// 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);

    /// 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;

    std::array<s8, 4> loop_filter_ref_deltas{};
    std::array<s8, 2> loop_filter_mode_deltas{};

    Vp9FrameContainer next_frame{};
    std::array<Vp9EntropyProbs, 4> frame_ctxs{};
    bool swap_ref_indices{};

    Vp9PictureInfo current_frame_info{};
    Vp9EntropyProbs prev_frame_probs{};
};

} // namespace Decoder
} // namespace Tegra


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <vector>

#include "common/common_funcs.h"
#include "common/common_types.h"

namespace Tegra {

namespace Decoder {
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 {
    u8 enabled;
    u8 update_map;
    u8 temporal_update;
    u8 abs_delta;
    std::array<u32, 8> feature_mask;
    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 {
    INSERT_PADDING_BYTES_NOINIT(1024);                 ///< 0x0000
    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,                               \
                  "Field " #field_name " has invalid 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,                                   \
                  "Field " #field_name " has invalid 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,                                  \
                  "Field " #field_name " has invalid 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

}; // namespace Decoder
}; // namespace Tegra


code:
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "common/assert.h"
#include "video_core/host1x/control.h"
#include "video_core/host1x/host1x.h"

namespace Tegra::Host1x {

Control::Control(Host1x& host1x_) : host1x(host1x_) {}

Control::~Control() = default;

void Control::ProcessMethod(Method method, u32 argument) {
    switch (method) {
    case Method::LoadSyncptPayload32:
        syncpoint_value = argument;
        break;
    case Method::WaitSyncpt:
    case Method::WaitSyncpt32:
        Execute(argument);
        break;
    default:
        UNIMPLEMENTED_MSG("Control method 0x{:X}", static_cast<u32>(method));
        break;
    }
}

void Control::Execute(u32 data) {
    host1x.GetSyncpointManager().WaitHost(data, syncpoint_value);
}

} // namespace Tegra::Host1x


code:
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "common/common_types.h"

namespace Tegra {

namespace Host1x {

class Host1x;
class Nvdec;

class Control {
public:
    enum class Method : u32 {
        WaitSyncpt = 0x8,
        LoadSyncptPayload32 = 0x4e,
        WaitSyncpt32 = 0x50,
    };

    explicit Control(Host1x& host1x);
    ~Control();

    /// Writes the method into the state, Invoke Execute() if encountered
    void ProcessMethod(Method method, u32 argument);

private:
    /// For Host1x, execute is waiting on a syncpoint previously written into the state
    void Execute(u32 data);

    u32 syncpoint_value{};
    Host1x& host1x;
};

} // namespace Host1x

} // namespace Tegra


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "video_core/host1x/ffmpeg/ffmpeg.h"

extern "C" {
#ifdef LIBVA_FOUND
// for querying VAAPI driver information
#include <libavutil/hwcontext_vaapi.h>
#endif
}

namespace FFmpeg {

namespace {

constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12;
constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P;
constexpr std::array PreferredGpuDecoders = {
    AV_HWDEVICE_TYPE_CUDA,
#ifdef _WIN32
    AV_HWDEVICE_TYPE_D3D11VA,
    AV_HWDEVICE_TYPE_DXVA2,
#elif defined(__unix__)
    AV_HWDEVICE_TYPE_VAAPI,
    AV_HWDEVICE_TYPE_VDPAU,
#endif
    // last resort for Linux Flatpak (w/ NVIDIA)
    AV_HWDEVICE_TYPE_VULKAN,
};

AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) {
    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 compatible GPU AV format, falling back to CPU");
    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;
}

} // namespace

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) != 0 &&
            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_codec_context = avcodec_alloc_context3(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 software decoding");
    }

    return true;
}

bool DecoderContext::SendPacket(const Packet& packet) {
    if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) {
        LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
        return false;
    }

    return true;
}

std::unique_ptr<Frame> DecoderContext::ReceiveFrame(bool* out_is_interlaced) {
    auto dst_frame = std::make_unique<Frame>();

    const auto ReceiveImpl = [&](AVFrame* frame) {
        if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) {
            LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
            return false;
        }

        *out_is_interlaced =
#if defined(FF_API_INTERLACED_FRAME) || LIBAVUTIL_VERSION_MAJOR >= 59
            (frame->flags & AV_FRAME_FLAG_INTERLACED) != 0;
#else
            frame->interlaced_frame != 0;
#endif
        return true;
    };

    if (m_codec_context->hw_device_ctx) {
        // If we have a hardware context, make a separate frame here to receive the
        // hardware result before sending it to the output.
        Frame intermediate_frame;

        if (!ReceiveImpl(intermediate_frame.GetFrame())) {
            return {};
        }

        dst_frame->SetFormat(PreferredGpuFormat);
        if (const int ret =
                av_hwframe_transfer_data(dst_frame->GetFrame(), intermediate_frame.GetFrame(), 0);
            ret < 0) {
            LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
            return {};
        }
    } else {
        // Otherwise, decode the frame as normal.
        if (!ReceiveImpl(dst_frame->GetFrame())) {
            return {};
        }
    }

    return dst_frame;
}

DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) {
    const AVFilter* buffer_src = avfilter_get_by_name("buffer");
    const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
    AVFilterInOut* inputs = avfilter_inout_alloc();
    AVFilterInOut* outputs = avfilter_inout_alloc();
    SCOPE_EXIT {
        avfilter_inout_free(&inputs);
        avfilter_inout_free(&outputs);
    };

    // Don't know how to get the accurate time_base but it doesn't matter for yadif filter
    // so just use 1/1 to make buffer filter happy
    std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame.GetWidth(),
                                   frame.GetHeight(), static_cast<int>(frame.GetPixelFormat()));

    m_filter_graph = avfilter_graph_alloc();
    int ret = avfilter_graph_create_filter(&m_source_context, buffer_src, "in", args.c_str(),
                                           nullptr, m_filter_graph);
    if (ret < 0) {
        LOG_ERROR(HW_GPU, "avfilter_graph_create_filter source error: {}", AVError(ret));
        return;
    }

    ret = avfilter_graph_create_filter(&m_sink_context, buffer_sink, "out", nullptr, nullptr,
                                       m_filter_graph);
    if (ret < 0) {
        LOG_ERROR(HW_GPU, "avfilter_graph_create_filter sink error: {}", AVError(ret));
        return;
    }

    inputs->name = av_strdup("out");
    inputs->filter_ctx = m_sink_context;
    inputs->pad_idx = 0;
    inputs->next = nullptr;

    outputs->name = av_strdup("in");
    outputs->filter_ctx = m_source_context;
    outputs->pad_idx = 0;
    outputs->next = nullptr;

    const char* description = "yadif=1:-1:0";
    ret = avfilter_graph_parse_ptr(m_filter_graph, description, &inputs, &outputs, nullptr);
    if (ret < 0) {
        LOG_ERROR(HW_GPU, "avfilter_graph_parse_ptr error: {}", AVError(ret));
        return;
    }

    ret = avfilter_graph_config(m_filter_graph, nullptr);
    if (ret < 0) {
        LOG_ERROR(HW_GPU, "avfilter_graph_config error: {}", AVError(ret));
        return;
    }

    m_initialized = true;
}

bool DeinterlaceFilter::AddSourceFrame(const Frame& frame) {
    if (const int ret = av_buffersrc_add_frame_flags(m_source_context, frame.GetFrame(),
                                                     AV_BUFFERSRC_FLAG_KEEP_REF);
        ret < 0) {
        LOG_ERROR(HW_GPU, "av_buffersrc_add_frame_flags error: {}", AVError(ret));
        return false;
    }

    return true;
}

std::unique_ptr<Frame> DeinterlaceFilter::DrainSinkFrame() {
    auto dst_frame = std::make_unique<Frame>();
    const int ret = av_buffersink_get_frame(m_sink_context, dst_frame->GetFrame());

    if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) {
        return {};
    }

    if (ret < 0) {
        LOG_ERROR(HW_GPU, "av_buffersink_get_frame error: {}", AVError(ret));
        return {};
    }

    return dst_frame;
}

DeinterlaceFilter::~DeinterlaceFilter() {
    avfilter_graph_free(&m_filter_graph);
}

void DecodeApi::Reset() {
    m_deinterlace_filter.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, size_t configuration_size) {
    FFmpeg::Packet packet(packet_data);
    return m_decoder_context->SendPacket(packet);
}

void DecodeApi::ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue) {
    // Receive raw frame from decoder.
    bool is_interlaced;
    auto frame = m_decoder_context->ReceiveFrame(&is_interlaced);
    if (!frame) {
        return;
    }

    if (!is_interlaced) {
        // If the frame is not interlaced, we can pend it now.
        frame_queue.push(std::move(frame));
    } else {
        // Create the deinterlacer if needed.
        if (!m_deinterlace_filter) {
            m_deinterlace_filter.emplace(*frame);
        }

        // Add the frame we just received.
        if (!m_deinterlace_filter->AddSourceFrame(*frame)) {
            return;
        }

        // Pend output fields.
        while (true) {
            auto filter_frame = m_deinterlace_filter->DrainSinkFrame();
            if (!filter_frame) {
                break;
            }

            frame_queue.push(std::move(filter_frame));
        }
    }
}

} // namespace FFmpeg


code:
// 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 <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>

#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
}

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];
    }

    u8** GetPlanes() const {
        return m_frame->data;
    }

    void SetFormat(int format) {
        m_frame->format = format;
    }

    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::unique_ptr<Frame> ReceiveFrame(bool* out_is_interlaced);

    AVCodecContext* GetCodecContext() const {
        return m_codec_context;
    }

private:
    AVCodecContext* m_codec_context{};
};

// Wraps an AVFilterGraph.
class DeinterlaceFilter {
public:
    YUZU_NON_COPYABLE(DeinterlaceFilter);
    YUZU_NON_MOVEABLE(DeinterlaceFilter);

    explicit DeinterlaceFilter(const Frame& frame);
    ~DeinterlaceFilter();

    bool AddSourceFrame(const Frame& frame);
    std::unique_ptr<Frame> DrainSinkFrame();

private:
    AVFilterGraph* m_filter_graph{};
    AVFilterContext* m_source_context{};
    AVFilterContext* m_sink_context{};
    bool m_initialized{};
};

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 SendPacket(std::span<const u8> packet_data, size_t configuration_size);
    void ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue);

private:
    std::optional<FFmpeg::Decoder> m_decoder;
    std::optional<FFmpeg::DecoderContext> m_decoder_context;
    std::optional<FFmpeg::HardwareContext> m_hardware_context;
    std::optional<FFmpeg::DeinterlaceFilter> m_deinterlace_filter;
};

} // namespace FFmpeg


code:
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/device_memory_manager.inc"
#include "video_core/host1x/gpu_device_memory_manager.h"
#include "video_core/rasterizer_interface.h"

namespace Tegra {

struct MaxwellDeviceMethods {
    static inline void MarkRegionCaching(Core::Memory::Memory* interface, VAddr address,
                                         size_t size, bool caching) {
        interface->RasterizerMarkRegionCached(address, size, caching);
    }
};

} // namespace Tegra

template struct Core::DeviceMemoryManagerAllocator<Tegra::MaxwellDeviceTraits>;
template class Core::DeviceMemoryManager<Tegra::MaxwellDeviceTraits>;

template const u8* Tegra::MaxwellDeviceMemoryManager::GetPointer<u8>(DAddr addr) const;
template u8* Tegra::MaxwellDeviceMemoryManager::GetPointer<u8>(DAddr addr);

template u8 Tegra::MaxwellDeviceMemoryManager::Read<u8>(DAddr addr) const;
template u16 Tegra::MaxwellDeviceMemoryManager::Read<u16>(DAddr addr) const;
template u32 Tegra::MaxwellDeviceMemoryManager::Read<u32>(DAddr addr) const;
template u64 Tegra::MaxwellDeviceMemoryManager::Read<u64>(DAddr addr) const;
template void Tegra::MaxwellDeviceMemoryManager::Write<u8>(DAddr addr, u8 data);
template void Tegra::MaxwellDeviceMemoryManager::Write<u16>(DAddr addr, u16 data);
template void Tegra::MaxwellDeviceMemoryManager::Write<u32>(DAddr addr, u32 data);
template void Tegra::MaxwellDeviceMemoryManager::Write<u64>(DAddr addr, u64 data);


code:
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/device_memory_manager.h"

namespace VideoCore {
class RasterizerInterface;
}

namespace Tegra {

struct MaxwellDeviceMethods;

struct MaxwellDeviceTraits {
    static constexpr size_t device_virtual_bits = 34;
    using DeviceInterface = typename VideoCore::RasterizerInterface;
    using DeviceMethods = MaxwellDeviceMethods;
};

using MaxwellDeviceMemoryManager = Core::DeviceMemoryManager<MaxwellDeviceTraits>;

} // namespace Tegra


code:
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "core/core.h"
#include "video_core/host1x/host1x.h"

namespace Tegra {

namespace 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)} {}

Host1x::~Host1x() = default;

} // namespace Host1x

} // namespace Tegra


code:
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "common/common_types.h"

#include "common/address_space.h"
#include "video_core/host1x/gpu_device_memory_manager.h"
#include "video_core/host1x/syncpoint_manager.h"
#include "video_core/memory_manager.h"

namespace Core {
class System;
} // namespace Core

namespace Tegra {

namespace Host1x {

class Host1x {
public:
    explicit Host1x(Core::System& system);
    ~Host1x();

    SyncpointManager& GetSyncpointManager() {
        return syncpoint_manager;
    }

    const SyncpointManager& GetSyncpointManager() const {
        return syncpoint_manager;
    }

    Tegra::MaxwellDeviceMemoryManager& MemoryManager() {
        return memory_manager;
    }

    const Tegra::MaxwellDeviceMemoryManager& MemoryManager() const {
        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;
    }

    const Common::FlatAllocator<u32, 0, 32>& Allocator() const {
        return *allocator;
    }

private:
    Core::System& system;
    SyncpointManager syncpoint_manager;
    Tegra::MaxwellDeviceMemoryManager memory_manager;
    Tegra::MemoryManager gmmu_manager;
    std::unique_ptr<Common::FlatAllocator<u32, 0, 32>> allocator;
};

} // namespace Host1x

} // namespace Tegra


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/assert.h"
#include "video_core/host1x/host1x.h"
#include "video_core/host1x/nvdec.h"

namespace Tegra::Host1x {

#define NVDEC_REG_INDEX(field_name)                                                                \
    (offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64))

Nvdec::Nvdec(Host1x& host1x_)
    : host1x(host1x_), state{}, codec(std::make_unique<Codec>(host1x, state)) {}

Nvdec::~Nvdec() = default;

void Nvdec::ProcessMethod(u32 method, u32 argument) {
    state.reg_array[method] = static_cast<u64>(argument) << 8;

    switch (method) {
    case NVDEC_REG_INDEX(set_codec_id):
        codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(argument));
        break;
    case NVDEC_REG_INDEX(execute):
        Execute();
        break;
    }
}

std::unique_ptr<FFmpeg::Frame> Nvdec::GetFrame() {
    return codec->GetCurrentFrame();
}

void Nvdec::Execute() {
    switch (codec->GetCurrentCodec()) {
    case NvdecCommon::VideoCodec::H264:
    case NvdecCommon::VideoCodec::VP8:
    case NvdecCommon::VideoCodec::VP9:
        codec->Decode();
        break;
    default:
        UNIMPLEMENTED_MSG("Codec {}", codec->GetCurrentCodecName());
        break;
    }
}

} // namespace Tegra::Host1x


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include <vector>
#include "common/common_types.h"
#include "video_core/host1x/codecs/codec.h"

namespace Tegra {

namespace Host1x {

class Host1x;

class Nvdec {
public:
    explicit Nvdec(Host1x& host1x);
    ~Nvdec();

    /// Writes the method into the state, Invoke Execute() if encountered
    void ProcessMethod(u32 method, u32 argument);

    /// Return most recently decoded frame
    [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetFrame();

private:
    /// Invoke codec to decode a frame
    void Execute();

    Host1x& host1x;
    NvdecCommon::NvdecRegisters state;
    std::unique_ptr<Codec> codec;
};

} // namespace Host1x

} // namespace Tegra


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"

namespace Tegra::Host1x::NvdecCommon {

enum class VideoCodec : u64 {
    None = 0x0,
    H264 = 0x3,
    VP8 = 0x5,
    H265 = 0x7,
    VP9 = 0x9,
};

// NVDEC should use a 32-bit address space, but is mapped to 64-bit,
// doubling the sizes here is compensating for that.
struct NvdecRegisters {
    static constexpr std::size_t NUM_REGS = 0x178;

    union {
        struct {
            INSERT_PADDING_WORDS_NOINIT(256); ///< 0x0000
            VideoCodec set_codec_id;          ///< 0x0400
            INSERT_PADDING_WORDS_NOINIT(126); ///< 0x0408
            u64 execute;                      ///< 0x0600
            INSERT_PADDING_WORDS_NOINIT(126); ///< 0x0608
            struct {                          ///< 0x0800
                union {
                    BitField<0, 3, VideoCodec> codec;
                    BitField<4, 1, u64> gp_timer_on;
                    BitField<13, 1, u64> mb_timer_on;
                    BitField<14, 1, u64> intra_frame_pslc;
                    BitField<17, 1, u64> all_intra_frame;
                };
            } control_params;
            u64 picture_info_offset;                   ///< 0x0808
            u64 frame_bitstream_offset;                ///< 0x0810
            u64 frame_number;                          ///< 0x0818
            u64 h264_slice_data_offsets;               ///< 0x0820
            u64 h264_mv_dump_offset;                   ///< 0x0828
            INSERT_PADDING_WORDS_NOINIT(6);            ///< 0x0830
            u64 frame_stats_offset;                    ///< 0x0848
            u64 h264_last_surface_luma_offset;         ///< 0x0850
            u64 h264_last_surface_chroma_offset;       ///< 0x0858
            std::array<u64, 17> surface_luma_offset;   ///< 0x0860
            std::array<u64, 17> surface_chroma_offset; ///< 0x08E8
            INSERT_PADDING_WORDS_NOINIT(68);           ///< 0x0970
            u64 vp8_prob_data_offset;                  ///< 0x0A80
            u64 vp8_header_partition_buf_offset;       ///< 0x0A88
            INSERT_PADDING_WORDS_NOINIT(60);           ///< 0x0A90
            u64 vp9_entropy_probs_offset;              ///< 0x0B80
            u64 vp9_backward_updates_offset;           ///< 0x0B88
            u64 vp9_last_frame_segmap_offset;          ///< 0x0B90
            u64 vp9_curr_frame_segmap_offset;          ///< 0x0B98
            INSERT_PADDING_WORDS_NOINIT(2);            ///< 0x0BA0
            u64 vp9_last_frame_mvs_offset;             ///< 0x0BA8
            u64 vp9_curr_frame_mvs_offset;             ///< 0x0BB0
            INSERT_PADDING_WORDS_NOINIT(2);            ///< 0x0BB8
        };
        std::array<u64, NUM_REGS> reg_array;
    };
};
static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size");

#define ASSERT_REG_POSITION(field_name, position)                                                  \
    static_assert(offsetof(NvdecRegisters, field_name) == position * sizeof(u64),                  \
                  "Field " #field_name " has invalid position")

ASSERT_REG_POSITION(set_codec_id, 0x80);
ASSERT_REG_POSITION(execute, 0xC0);
ASSERT_REG_POSITION(control_params, 0x100);
ASSERT_REG_POSITION(picture_info_offset, 0x101);
ASSERT_REG_POSITION(frame_bitstream_offset, 0x102);
ASSERT_REG_POSITION(frame_number, 0x103);
ASSERT_REG_POSITION(h264_slice_data_offsets, 0x104);
ASSERT_REG_POSITION(frame_stats_offset, 0x109);
ASSERT_REG_POSITION(h264_last_surface_luma_offset, 0x10A);
ASSERT_REG_POSITION(h264_last_surface_chroma_offset, 0x10B);
ASSERT_REG_POSITION(surface_luma_offset, 0x10C);
ASSERT_REG_POSITION(surface_chroma_offset, 0x11D);
ASSERT_REG_POSITION(vp8_prob_data_offset, 0x150);
ASSERT_REG_POSITION(vp8_header_partition_buf_offset, 0x151);
ASSERT_REG_POSITION(vp9_entropy_probs_offset, 0x170);
ASSERT_REG_POSITION(vp9_backward_updates_offset, 0x171);
ASSERT_REG_POSITION(vp9_last_frame_segmap_offset, 0x172);
ASSERT_REG_POSITION(vp9_curr_frame_segmap_offset, 0x173);
ASSERT_REG_POSITION(vp9_last_frame_mvs_offset, 0x175);
ASSERT_REG_POSITION(vp9_curr_frame_mvs_offset, 0x176);

#undef ASSERT_REG_POSITION

} // namespace Tegra::Host1x::NvdecCommon


code:
// SPDX-FileCopyrightText: Ryujinx Team and Contributors
// SPDX-License-Identifier: MIT

#include <algorithm>
#include "sync_manager.h"
#include "video_core/host1x/host1x.h"
#include "video_core/host1x/syncpoint_manager.h"

namespace Tegra {
namespace Host1x {

SyncptIncrManager::SyncptIncrManager(Host1x& host1x_) : host1x(host1x_) {}
SyncptIncrManager::~SyncptIncrManager() = default;

void SyncptIncrManager::Increment(u32 id) {
    increments.emplace_back(0, 0, id, true);
    IncrementAllDone();
}

u32 SyncptIncrManager::IncrementWhenDone(u32 class_id, u32 id) {
    const u32 handle = current_id++;
    increments.emplace_back(handle, class_id, id);
    return handle;
}

void SyncptIncrManager::SignalDone(u32 handle) {
    const auto done_incr =
        std::find_if(increments.begin(), increments.end(),
                     [handle](const SyncptIncr& incr) { return incr.id == handle; });
    if (done_incr != increments.cend()) {
        done_incr->complete = true;
    }
    IncrementAllDone();
}

void SyncptIncrManager::IncrementAllDone() {
    std::size_t done_count = 0;
    for (; done_count < increments.size(); ++done_count) {
        if (!increments[done_count].complete) {
            break;
        }
        auto& syncpoint_manager = host1x.GetSyncpointManager();
        syncpoint_manager.IncrementGuest(increments[done_count].syncpt_id);
        syncpoint_manager.IncrementHost(increments[done_count].syncpt_id);
    }
    increments.erase(increments.begin(), increments.begin() + done_count);
}

} // namespace Host1x
} // namespace Tegra


code:
// SPDX-FileCopyrightText: Ryujinx Team and Contributors
// SPDX-License-Identifier: MIT

#pragma once

#include <mutex>
#include <vector>
#include "common/common_types.h"

namespace Tegra {

namespace Host1x {

class Host1x;

struct SyncptIncr {
    u32 id;
    u32 class_id;
    u32 syncpt_id;
    bool complete;

    SyncptIncr(u32 id_, u32 class_id_, u32 syncpt_id_, bool done = false)
        : id(id_), class_id(class_id_), syncpt_id(syncpt_id_), complete(done) {}
};

class SyncptIncrManager {
public:
    explicit SyncptIncrManager(Host1x& host1x);
    ~SyncptIncrManager();

    /// Add syncpoint id and increment all
    void Increment(u32 id);

    /// Returns a handle to increment later
    u32 IncrementWhenDone(u32 class_id, u32 id);

    /// IncrememntAllDone, including handle
    void SignalDone(u32 handle);

    /// Increment all sequential pending increments that are already done.
    void IncrementAllDone();

private:
    std::vector<SyncptIncr> increments;
    std::mutex increment_lock;
    u32 current_id{};

    Host1x& host1x;
};

} // namespace Host1x

} // namespace Tegra


code:
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "common/microprofile.h"
#include "video_core/host1x/syncpoint_manager.h"

namespace Tegra {

namespace Host1x {

MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));

SyncpointManager::ActionHandle SyncpointManager::RegisterAction(
    std::atomic<u32>& syncpoint, std::list<RegisteredAction>& action_storage, u32 expected_value,
    std::function<void()>&& action) {
    if (syncpoint.load(std::memory_order_acquire) >= expected_value) {
        action();
        return {};
    }

    std::unique_lock lk(guard);
    if (syncpoint.load(std::memory_order_relaxed) >= expected_value) {
        action();
        return {};
    }
    auto it = action_storage.begin();
    while (it != action_storage.end()) {
        if (it->expected_value >= expected_value) {
            break;
        }
        ++it;
    }
    return action_storage.emplace(it, expected_value, std::move(action));
}

void SyncpointManager::DeregisterAction(std::list<RegisteredAction>& action_storage,
                                        const ActionHandle& handle) {
    std::unique_lock lk(guard);

    // We want to ensure the iterator still exists prior to erasing it
    // Otherwise, if an invalid iterator was passed in then it could lead to UB
    // It is important to avoid UB in that case since the deregister isn't called from a locked
    // context
    for (auto it = action_storage.begin(); it != action_storage.end(); it++) {
        if (it == handle) {
            action_storage.erase(it);
            return;
        }
    }
}

void SyncpointManager::DeregisterGuestAction(u32 syncpoint_id, const ActionHandle& handle) {
    DeregisterAction(guest_action_storage[syncpoint_id], handle);
}

void SyncpointManager::DeregisterHostAction(u32 syncpoint_id, const ActionHandle& handle) {
    DeregisterAction(host_action_storage[syncpoint_id], handle);
}

void SyncpointManager::IncrementGuest(u32 syncpoint_id) {
    Increment(syncpoints_guest[syncpoint_id], wait_guest_cv, guest_action_storage[syncpoint_id]);
}

void SyncpointManager::IncrementHost(u32 syncpoint_id) {
    Increment(syncpoints_host[syncpoint_id], wait_host_cv, host_action_storage[syncpoint_id]);
}

void SyncpointManager::WaitGuest(u32 syncpoint_id, u32 expected_value) {
    Wait(syncpoints_guest[syncpoint_id], wait_guest_cv, expected_value);
}

void SyncpointManager::WaitHost(u32 syncpoint_id, u32 expected_value) {
    MICROPROFILE_SCOPE(GPU_wait);
    Wait(syncpoints_host[syncpoint_id], wait_host_cv, expected_value);
}

void SyncpointManager::Increment(std::atomic<u32>& syncpoint, std::condition_variable& wait_cv,
                                 std::list<RegisteredAction>& action_storage) {
    auto new_value{syncpoint.fetch_add(1, std::memory_order_acq_rel) + 1};

    std::unique_lock lk(guard);
    auto it = action_storage.begin();
    while (it != action_storage.end()) {
        if (it->expected_value > new_value) {
            break;
        }
        it->action();
        it = action_storage.erase(it);
    }
    wait_cv.notify_all();
}

void SyncpointManager::Wait(std::atomic<u32>& syncpoint, std::condition_variable& wait_cv,
                            u32 expected_value) {
    const auto pred = [&]() { return syncpoint.load(std::memory_order_acquire) >= expected_value; };
    if (pred()) {
        return;
    }

    std::unique_lock lk(guard);
    wait_cv.wait(lk, pred);
}

} // namespace Host1x

} // namespace Tegra