mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-26 00:56:45 +00:00
[android] Verifying the actual flag required on hardware access
This commit is contained in:
@@ -27,7 +27,7 @@ if (ARCHITECTURE_arm64)
|
||||
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
||||
endif()
|
||||
|
||||
target_link_libraries(yuzu-android PRIVATE OpenSSL::SSL cpp-jwt::cpp-jwt)
|
||||
target_link_libraries(yuzu-android PRIVATE ${FFmpeg_LIBRARIES} OpenSSL::SSL cpp-jwt::cpp-jwt)
|
||||
if (ENABLE_UPDATE_CHECKER)
|
||||
target_compile_definitions(yuzu-android PUBLIC ENABLE_UPDATE_CHECKER)
|
||||
endif()
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
#include <frontend_common/content_manager.h>
|
||||
#include <jni.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/jni.h>
|
||||
}
|
||||
|
||||
#include "common/android/multiplayer/multiplayer.h"
|
||||
#include "common/android/android_common.h"
|
||||
#include "common/android/id_cache.h"
|
||||
@@ -680,6 +684,13 @@ const char* fallback_cpu_detection() {
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
jint InitFFmpegOnLoad(JavaVM* vm) {
|
||||
av_jni_set_java_vm(vm, nullptr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
|
||||
|
||||
@@ -123,10 +123,6 @@ namespace Common::Android {
|
||||
return owned.env;
|
||||
}
|
||||
|
||||
JavaVM *GetJavaVM() {
|
||||
return s_java_vm;
|
||||
}
|
||||
|
||||
jclass GetNativeLibraryClass() {
|
||||
return s_native_library_class;
|
||||
}
|
||||
@@ -431,8 +427,11 @@ namespace Common::Android {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
jint InitFFmpegOnLoad(JavaVM *vm);
|
||||
|
||||
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
s_java_vm = vm;
|
||||
InitFFmpegOnLoad(vm);
|
||||
|
||||
JNIEnv *env;
|
||||
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -12,7 +12,6 @@
|
||||
namespace Common::Android {
|
||||
|
||||
JNIEnv* GetEnvForThread();
|
||||
JavaVM* GetJavaVM();
|
||||
|
||||
/**
|
||||
* Starts a new thread to run JNI. Intended to be used when you must run JNI from a fiber.
|
||||
|
||||
@@ -20,58 +20,54 @@ Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::Nv
|
||||
|
||||
Decoder::~Decoder() = default;
|
||||
|
||||
void Decoder::SetFrameDimensions(s32 width, s32 height) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
frame_dimensions.reset();
|
||||
return;
|
||||
}
|
||||
frame_dimensions = FFmpeg::FrameDimensions{width, height};
|
||||
}
|
||||
|
||||
void Decoder::Decode() {
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto packet_data = ComposeFrame();
|
||||
// Send assembled bitstream to decoder.
|
||||
if (!decode_api.SendPacket(packet_data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only receive/store visible frames.
|
||||
if (vp9_hidden_frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Receive output frames from decoder.
|
||||
auto frame = decode_api.ReceiveFrame();
|
||||
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsInterlaced()) {
|
||||
auto [luma_top, luma_bottom, chroma_top, chroma_bottom] = GetInterlacedOffsets();
|
||||
auto frame_copy = frame;
|
||||
|
||||
if (!frame.get()) {
|
||||
LOG_ERROR(HW_GPU,
|
||||
"Nvdec {} failed to decode interlaced frame for top {:#X} bottom 0x{:X}", id,
|
||||
luma_top, luma_bottom);
|
||||
}
|
||||
|
||||
if (UsingDecodeOrder()) {
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy));
|
||||
} else {
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_top, std::move(frame));
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy));
|
||||
}
|
||||
FFmpeg::FrameOffsets offsets{};
|
||||
offsets.hidden = vp9_hidden_frame;
|
||||
offsets.interlaced = IsInterlaced();
|
||||
if (offsets.interlaced) {
|
||||
std::tie(offsets.luma, offsets.luma_bottom, std::ignore, std::ignore) =
|
||||
GetInterlacedOffsets();
|
||||
} else {
|
||||
auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
|
||||
std::tie(offsets.luma, std::ignore) = GetProgressiveOffsets();
|
||||
}
|
||||
|
||||
if (!frame.get()) {
|
||||
LOG_ERROR(HW_GPU, "Nvdec {} failed to decode progressive frame for luma {:#X}", id,
|
||||
luma_offset);
|
||||
}
|
||||
// Send assembled bitstream to decoder.
|
||||
if (!decode_api.SendPacket(packet_data, offsets, GetFrameDimensions())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto push = [&](u64 luma, std::shared_ptr<FFmpeg::Frame> frame) {
|
||||
if (UsingDecodeOrder()) {
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame));
|
||||
host1x.frame_queue.PushDecodeOrder(id, luma, std::move(frame));
|
||||
} else {
|
||||
host1x.frame_queue.PushPresentOrder(id, luma_offset, std::move(frame));
|
||||
host1x.frame_queue.PushPresentOrder(id, luma, std::move(frame));
|
||||
}
|
||||
};
|
||||
|
||||
while (auto result = decode_api.ReceiveFrame()) {
|
||||
auto& [frame, frame_offsets] = *result;
|
||||
if (frame_offsets.hidden || !frame) {
|
||||
continue;
|
||||
}
|
||||
if (frame_offsets.interlaced) {
|
||||
auto frame_copy = frame;
|
||||
push(frame_offsets.luma, std::move(frame));
|
||||
push(frame_offsets.luma_bottom, std::move(frame_copy));
|
||||
} else {
|
||||
push(frame_offsets.luma, std::move(frame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <queue>
|
||||
|
||||
@@ -46,12 +47,19 @@ protected:
|
||||
virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0;
|
||||
virtual bool IsInterlaced() = 0;
|
||||
|
||||
void SetFrameDimensions(s32 width, s32 height);
|
||||
|
||||
std::optional<FFmpeg::FrameDimensions> GetFrameDimensions() const {
|
||||
return frame_dimensions;
|
||||
}
|
||||
|
||||
FFmpeg::DecodeApi decode_api;
|
||||
Host1x::Host1x& host1x;
|
||||
const Host1x::NvdecCommon::NvdecRegisters& regs;
|
||||
s32 id;
|
||||
bool initialized : 1 = false;
|
||||
bool vp9_hidden_frame : 1 = false;
|
||||
std::optional<FFmpeg::FrameDimensions> frame_dimensions;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -52,6 +52,10 @@ bool H264::IsInterlaced() {
|
||||
|
||||
std::span<const u8> H264::ComposeFrame() {
|
||||
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), ¤t_context, sizeof(H264DecoderContext));
|
||||
const auto& params = current_context.h264_parameter_set;
|
||||
SetFrameDimensions(static_cast<s32>(params.pic_width_in_mbs) * 16,
|
||||
static_cast<s32>(params.frame_height_in_mbs) * 16);
|
||||
|
||||
const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
|
||||
if (!is_first_frame && frame_number != 0) {
|
||||
frame_scratch.resize_destructive(current_context.stream_len);
|
||||
|
||||
@@ -35,6 +35,7 @@ std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() {
|
||||
|
||||
std::span<const u8> VP8::ComposeFrame() {
|
||||
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), ¤t_context, sizeof(VP8PictureInfo));
|
||||
SetFrameDimensions(current_context.frame_width, current_context.frame_height);
|
||||
|
||||
const bool is_key_frame = current_context.key_frame == 1u;
|
||||
const auto bitstream_size = size_t(current_context.vld_buffer_size);
|
||||
|
||||
@@ -841,6 +841,7 @@ std::span<const u8> VP9::ComposeFrame() {
|
||||
{
|
||||
Vp9FrameContainer curr_frame = GetCurrentFrame();
|
||||
current_frame_info = curr_frame.info;
|
||||
SetFrameDimensions(current_frame_info.frame_size.width, current_frame_info.frame_size.height);
|
||||
bitstream = std::move(curr_frame.bit_stream);
|
||||
}
|
||||
// The uncompressed header routine sets PrevProb parameters needed for the compressed header
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/scope_exit.h"
|
||||
@@ -19,16 +23,8 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <libavutil/hwcontext.h>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <libavcodec/jni.h>
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include "common/android/id_cache.h"
|
||||
#endif
|
||||
|
||||
namespace FFmpeg {
|
||||
|
||||
namespace {
|
||||
@@ -92,6 +88,52 @@ std::string AVError(int errnum) {
|
||||
return errbuf;
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
size_t FindNalStartCode(std::span<const u8> data, size_t i) {
|
||||
const size_t n = data.size();
|
||||
if (i + 3 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 0 && data[i + 3] == 1) {
|
||||
return 4;
|
||||
}
|
||||
if (i + 2 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<u8> ExtractH264ParameterSetExtradata(std::span<const u8> packet) {
|
||||
std::vector<u8> extradata;
|
||||
const size_t size = packet.size();
|
||||
size_t i = 0;
|
||||
while (i < size) {
|
||||
const size_t sc = FindNalStartCode(packet, i);
|
||||
if (sc == 0) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
const size_t nal_start = i + sc;
|
||||
if (nal_start >= size) {
|
||||
break;
|
||||
}
|
||||
const u8 nal_type = packet[nal_start] & 0x1F;
|
||||
|
||||
size_t j = nal_start + 1;
|
||||
while (j < size && FindNalStartCode(packet, j) == 0) {
|
||||
++j;
|
||||
}
|
||||
|
||||
if (nal_type == 7 || nal_type == 8) {
|
||||
constexpr u8 start[4] = {0, 0, 0, 1};
|
||||
extradata.insert(extradata.end(), start, start + sizeof(start));
|
||||
extradata.insert(extradata.end(), packet.begin() + nal_start, packet.begin() + j);
|
||||
} else if (nal_type == 1 || nal_type == 5) {
|
||||
break;
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
return extradata;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
Packet::Packet(std::span<const u8> data) {
|
||||
@@ -126,7 +168,24 @@ Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||
return AV_CODEC_ID_NONE;
|
||||
}
|
||||
}();
|
||||
m_codec = avcodec_find_decoder(av_codec);
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
|
||||
const char* mc_name = nullptr;
|
||||
switch (av_codec) {
|
||||
case AV_CODEC_ID_H264: mc_name = "h264_mediacodec"; break;
|
||||
case AV_CODEC_ID_VP8: mc_name = "vp8_mediacodec"; break;
|
||||
case AV_CODEC_ID_VP9: mc_name = "vp9_mediacodec"; break;
|
||||
default: break;
|
||||
}
|
||||
if (mc_name) {
|
||||
m_codec = avcodec_find_decoder_by_name(mc_name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!m_codec) {
|
||||
m_codec = avcodec_find_decoder(av_codec);
|
||||
}
|
||||
}
|
||||
|
||||
bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
|
||||
@@ -222,6 +281,8 @@ DecoderContext::DecoderContext(const Decoder& decoder) : m_decoder{decoder} {
|
||||
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;
|
||||
m_codec_context->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
||||
m_codec_context->flags2 |= AV_CODEC_FLAG2_FAST;
|
||||
}
|
||||
|
||||
DecoderContext::~DecoderContext() {
|
||||
@@ -235,7 +296,19 @@ void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, A
|
||||
m_codec_context->pix_fmt = hw_pix_fmt;
|
||||
}
|
||||
|
||||
bool DecoderContext::OpenContext(const Decoder& decoder) {
|
||||
bool DecoderContext::OpenContext(const Decoder& decoder, std::span<const u8> extradata) {
|
||||
if (!extradata.empty()) {
|
||||
av_freep(&m_codec_context->extradata);
|
||||
m_codec_context->extradata = static_cast<u8*>(
|
||||
av_mallocz(extradata.size() + AV_INPUT_BUFFER_PADDING_SIZE));
|
||||
if (!m_codec_context->extradata) {
|
||||
LOG_ERROR(HW_GPU, "Failed to allocate extradata");
|
||||
return false;
|
||||
}
|
||||
std::memcpy(m_codec_context->extradata, extradata.data(), extradata.size());
|
||||
m_codec_context->extradata_size = static_cast<int>(extradata.size());
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -289,6 +362,13 @@ void DecodeApi::Reset() {
|
||||
m_hardware_context.reset();
|
||||
m_decoder_context.reset();
|
||||
m_decoder.reset();
|
||||
m_opened = false;
|
||||
m_defer_android_mediacodec_open = false;
|
||||
m_needs_h264_extradata = false;
|
||||
m_next_pts = 0;
|
||||
while (!m_pending_offsets.empty()) {
|
||||
m_pending_offsets.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||
@@ -298,32 +378,82 @@ bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
|
||||
|
||||
// Enable GPU decoding if requested.
|
||||
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
|
||||
#if defined(__ANDROID__)
|
||||
static const int jni_vm_result =
|
||||
av_jni_set_java_vm(Common::Android::GetJavaVM(), nullptr);
|
||||
(void)jni_vm_result;
|
||||
#endif
|
||||
m_hardware_context.emplace();
|
||||
m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
const std::string_view decoder_name = m_decoder->GetCodec() ? m_decoder->GetCodec()->name : "";
|
||||
|
||||
m_defer_android_mediacodec_open = decoder_name == "h264_mediacodec" ||
|
||||
decoder_name == "vp8_mediacodec" ||
|
||||
decoder_name == "vp9_mediacodec";
|
||||
|
||||
m_needs_h264_extradata = decoder_name == "h264_mediacodec";
|
||||
if (m_defer_android_mediacodec_open) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Open the decoder context.
|
||||
if (!m_decoder_context->OpenContext(*m_decoder)) {
|
||||
this->Reset();
|
||||
return false;
|
||||
}
|
||||
m_opened = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecodeApi::SendPacket(std::span<const u8> packet_data) {
|
||||
bool DecodeApi::SendPacket(std::span<const u8> packet_data, const FrameOffsets& offsets,
|
||||
std::optional<FrameDimensions> dimensions) {
|
||||
if (!m_opened) {
|
||||
std::vector<u8> extradata;
|
||||
#if defined(__ANDROID__)
|
||||
if (m_defer_android_mediacodec_open) {
|
||||
if (!dimensions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* ctx = m_decoder_context->GetCodecContext();
|
||||
ctx->width = dimensions->width;
|
||||
ctx->height = dimensions->height;
|
||||
ctx->coded_width = dimensions->width;
|
||||
ctx->coded_height = dimensions->height;
|
||||
}
|
||||
|
||||
if (m_needs_h264_extradata) {
|
||||
extradata = ExtractH264ParameterSetExtradata(packet_data);
|
||||
if (extradata.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!m_decoder_context->OpenContext(*m_decoder, extradata)) {
|
||||
this->Reset();
|
||||
return false;
|
||||
}
|
||||
m_opened = true;
|
||||
}
|
||||
m_pending_offsets.push(offsets);
|
||||
FFmpeg::Packet packet(packet_data);
|
||||
packet.GetPacket()->pts = m_next_pts;
|
||||
packet.GetPacket()->dts = m_next_pts;
|
||||
++m_next_pts;
|
||||
return m_decoder_context->SendPacket(packet);
|
||||
}
|
||||
|
||||
std::shared_ptr<Frame> DecodeApi::ReceiveFrame() {
|
||||
// Receive raw frame from decoder.
|
||||
return m_decoder_context->ReceiveFrame();
|
||||
std::optional<DecodeApi::DecodedFrame> DecodeApi::ReceiveFrame() {
|
||||
auto frame = m_decoder_context->ReceiveFrame();
|
||||
if (!frame) {
|
||||
return std::nullopt;
|
||||
}
|
||||
FrameOffsets offsets{};
|
||||
if (!m_pending_offsets.empty()) {
|
||||
offsets = m_pending_offsets.front();
|
||||
m_pending_offsets.pop();
|
||||
}
|
||||
return DecodedFrame{std::move(frame), offsets};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ public:
|
||||
~DecoderContext();
|
||||
|
||||
void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
|
||||
bool OpenContext(const Decoder& decoder);
|
||||
bool OpenContext(const Decoder& decoder, std::span<const u8> extradata = {});
|
||||
bool SendPacket(const Packet& packet);
|
||||
std::shared_ptr<Frame> ReceiveFrame();
|
||||
|
||||
@@ -198,6 +198,18 @@ private:
|
||||
bool m_decode_order{};
|
||||
};
|
||||
|
||||
struct FrameOffsets {
|
||||
bool interlaced{};
|
||||
bool hidden{};
|
||||
u64 luma{};
|
||||
u64 luma_bottom{};
|
||||
};
|
||||
|
||||
struct FrameDimensions {
|
||||
s32 width{};
|
||||
s32 height{};
|
||||
};
|
||||
|
||||
class DecodeApi {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(DecodeApi);
|
||||
@@ -213,13 +225,24 @@ public:
|
||||
return m_decoder_context->UsingDecodeOrder();
|
||||
}
|
||||
|
||||
bool SendPacket(std::span<const u8> packet_data);
|
||||
std::shared_ptr<Frame> ReceiveFrame();
|
||||
bool SendPacket(std::span<const u8> packet_data, const FrameOffsets& offsets,
|
||||
std::optional<FrameDimensions> dimensions = std::nullopt);
|
||||
|
||||
struct DecodedFrame {
|
||||
std::shared_ptr<Frame> frame;
|
||||
FrameOffsets offsets;
|
||||
};
|
||||
std::optional<DecodedFrame> ReceiveFrame();
|
||||
|
||||
private:
|
||||
std::optional<FFmpeg::Decoder> m_decoder;
|
||||
std::optional<FFmpeg::DecoderContext> m_decoder_context;
|
||||
std::optional<FFmpeg::HardwareContext> m_hardware_context;
|
||||
bool m_opened{};
|
||||
bool m_defer_android_mediacodec_open{};
|
||||
bool m_needs_h264_extradata{};
|
||||
s64 m_next_pts{};
|
||||
std::queue<FrameOffsets> m_pending_offsets;
|
||||
};
|
||||
|
||||
} // namespace FFmpeg
|
||||
|
||||
@@ -31,6 +31,7 @@ Nvdec::Nvdec(Host1x& host1x_, s32 id_, u32 syncpt)
|
||||
|
||||
Nvdec::~Nvdec() {
|
||||
LOG_INFO(HW_GPU, "Destroying nvdec {}", id);
|
||||
host1x.frame_queue.Close(id);
|
||||
}
|
||||
|
||||
void Nvdec::ProcessMethod(u32 method, u32 argument) {
|
||||
|
||||
@@ -118,6 +118,7 @@ void Vic::Execute() noexcept {
|
||||
output_surface.resize(output_width * output_height);
|
||||
|
||||
if (Settings::values.nvdec_emulation.GetValue() != Settings::NvdecEmulation::Off) {
|
||||
bool decoded_frame = false;
|
||||
for (size_t i = 0; i < config.slot_structs.size(); i++) {
|
||||
if (auto& slot_config = config.slot_structs[i]; slot_config.config.slot_enable) {
|
||||
auto const luma_offset = regs.surfaces[i][SurfaceIndex::Current].luma.Address();
|
||||
@@ -136,11 +137,17 @@ void Vic::Execute() noexcept {
|
||||
break;
|
||||
}
|
||||
Blend(config, slot_config, config.output_surface_config.out_pixel_format);
|
||||
decoded_frame = true;
|
||||
} else {
|
||||
LOG_ERROR(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset);
|
||||
LOG_TRACE(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (decoded_frame) {
|
||||
has_decoded_frame = true;
|
||||
} else if (!has_decoded_frame) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Fill the frame with black, as otherwise they can have random data and be very glitchy.
|
||||
std::fill(output_surface.begin(), output_surface.end(), Pixel{});
|
||||
|
||||
@@ -628,6 +628,7 @@ private:
|
||||
|
||||
s32 id;
|
||||
s32 nvdec_id{-1};
|
||||
bool has_decoded_frame{};
|
||||
u32 syncpoint;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user