diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index 7908e0a619..0baff02c84 100644 --- a/externals/ffmpeg/CMakeLists.txt +++ b/externals/ffmpeg/CMakeLists.txt @@ -59,6 +59,11 @@ endif() if (PLATFORM_PS4 OR PLATFORM_MANAGARM) # Doesn't support VA-API, don't go thru the embarrassment of trying to enable it list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi) +elseif (ANDROID) + list(APPEND FFmpeg_HWACCEL_FLAGS + --enable-mediacodec + --enable-jni + ) elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBVA libva) diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index b6fe2d7722..7013691765 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -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() diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index f40177ed03..41ba1b2019 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -36,6 +36,10 @@ #include #include +extern "C" { +#include +} + #include "common/android/multiplayer/multiplayer.h" #include "common/android/android_common.h" #include "common/android/id_cache.h" @@ -678,6 +682,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, diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp index 76af1e0fb2..0fb358f6e4 100644 --- a/src/common/android/id_cache.cpp +++ b/src/common/android/id_cache.cpp @@ -427,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(&env), JNI_VERSION) != JNI_OK) diff --git a/src/common/android/id_cache.h b/src/common/android/id_cache.h index 6b48f471b7..6e47acb746 100644 --- a/src/common/android/id_cache.h +++ b/src/common/android/id_cache.h @@ -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 diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index f73fb233cb..7ab2bd4adf 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -435,7 +435,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct } if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles) && - profile.support_vote) { + profile.support_vote && profile.SupportsSubgroupStage(ctx.stage)) { ctx.AddCapability(spv::Capability::GroupNonUniformBallot); ctx.AddCapability(spv::Capability::GroupNonUniformShuffle); if (!profile.warp_size_potentially_larger_than_guest) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp index bdcbccfde9..4a5e7ef067 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -156,7 +159,8 @@ void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) { } Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { - if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) { + if (ctx.profile.support_int8 && ctx.profile.support_uniform_and_storage_buffer_8bit && + ctx.profile.support_descriptor_aliasing) { return ctx.OpUConvert(ctx.U32[1], LoadStorage(ctx, binding, offset, ctx.U8, ctx.storage_types.U8, sizeof(u8), &StorageDefinitions::U8)); @@ -167,7 +171,8 @@ Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value } Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { - if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) { + if (ctx.profile.support_int8 && ctx.profile.support_uniform_and_storage_buffer_8bit && + ctx.profile.support_descriptor_aliasing) { return ctx.OpSConvert(ctx.U32[1], LoadStorage(ctx, binding, offset, ctx.S8, ctx.storage_types.S8, sizeof(s8), &StorageDefinitions::S8)); @@ -178,7 +183,8 @@ Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value } Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { - if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) { + if (ctx.profile.support_int16 && ctx.profile.support_uniform_and_storage_buffer_16bit && + ctx.profile.support_descriptor_aliasing) { return ctx.OpUConvert(ctx.U32[1], LoadStorage(ctx, binding, offset, ctx.U16, ctx.storage_types.U16, sizeof(u16), &StorageDefinitions::U16)); @@ -189,7 +195,8 @@ Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Valu } Id EmitLoadStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { - if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) { + if (ctx.profile.support_int16 && ctx.profile.support_uniform_and_storage_buffer_16bit && + ctx.profile.support_descriptor_aliasing) { return ctx.OpSConvert(ctx.U32[1], LoadStorage(ctx, binding, offset, ctx.S16, ctx.storage_types.S16, sizeof(s16), &StorageDefinitions::S16)); @@ -227,7 +234,7 @@ Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Valu void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value) { - if (ctx.profile.support_int8) { + if (ctx.profile.support_int8 && ctx.profile.support_uniform_and_storage_buffer_8bit) { WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8, sizeof(u8), &StorageDefinitions::U8); } else { @@ -237,7 +244,7 @@ void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Va void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value) { - if (ctx.profile.support_int8) { + if (ctx.profile.support_int8 && ctx.profile.support_uniform_and_storage_buffer_8bit) { WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8, sizeof(s8), &StorageDefinitions::S8); } else { @@ -247,7 +254,7 @@ void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Va void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value) { - if (ctx.profile.support_int16) { + if (ctx.profile.support_int16 && ctx.profile.support_uniform_and_storage_buffer_16bit) { WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16, sizeof(u16), &StorageDefinitions::U16); } else { @@ -257,7 +264,7 @@ void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::V void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value) { - if (ctx.profile.support_int16) { + if (ctx.profile.support_int16 && ctx.profile.support_uniform_and_storage_buffer_16bit) { WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16, sizeof(s16), &StorageDefinitions::S16); } else { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp index df05dad74a..c3208b955a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp index 77ff8c5731..242426ec08 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,7 +13,14 @@ Id SubgroupScope(EmitContext& ctx) { return ctx.Const(static_cast(spv::Scope::Subgroup)); } +bool StageSupportsSubgroups(EmitContext& ctx) { + return ctx.profile.SupportsSubgroupStage(ctx.stage); +} + Id GetThreadId(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.u32_zero_value; + } return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); } @@ -68,6 +78,9 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask } Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { + if (!StageSupportsSubgroups(ctx)) { + return value; + } return ctx.OpSelect( ctx.U32[1], in_range, ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value); @@ -89,6 +102,9 @@ Id EmitLaneId(EmitContext& ctx) { } Id EmitVoteAll(EmitContext& ctx, Id pred) { + if (!StageSupportsSubgroups(ctx)) { + return pred; + } if (!ctx.profile.warp_size_potentially_larger_than_guest) { return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred); } @@ -102,6 +118,9 @@ Id EmitVoteAll(EmitContext& ctx, Id pred) { } Id EmitVoteAny(EmitContext& ctx, Id pred) { + if (!StageSupportsSubgroups(ctx)) { + return pred; + } if (!ctx.profile.warp_size_potentially_larger_than_guest) { return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred); } @@ -115,6 +134,9 @@ Id EmitVoteAny(EmitContext& ctx, Id pred) { } Id EmitVoteEqual(EmitContext& ctx, Id pred) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.true_value; + } if (!ctx.profile.warp_size_potentially_larger_than_guest) { return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred); } @@ -129,6 +151,9 @@ Id EmitVoteEqual(EmitContext& ctx, Id pred) { } Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.OpSelect(ctx.U32[1], pred, ctx.Const(1u), ctx.u32_zero_value); + } const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)}; if (!ctx.profile.warp_size_potentially_larger_than_guest) { return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); @@ -137,22 +162,37 @@ Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { } Id EmitSubgroupEqMask(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.Const(1u); + } return LoadMask(ctx, ctx.subgroup_mask_eq); } Id EmitSubgroupLtMask(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.u32_zero_value; + } return LoadMask(ctx, ctx.subgroup_mask_lt); } Id EmitSubgroupLeMask(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.Const(1u); + } return LoadMask(ctx, ctx.subgroup_mask_le); } Id EmitSubgroupGtMask(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.u32_zero_value; + } return LoadMask(ctx, ctx.subgroup_mask_gt); } Id EmitSubgroupGeMask(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.Const(1u); + } return LoadMask(ctx, ctx.subgroup_mask_ge); } @@ -222,7 +262,7 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) { const Id three{ctx.Const(3U)}; - Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; + Id mask{GetThreadId(ctx)}; mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three); mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U)); mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index b1bed263a6..17ce4d744f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -1231,13 +1231,15 @@ void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) { const IR::Type used_types{profile.support_descriptor_aliasing ? info.used_storage_buffer_types : IR::Type::U32}; - if (profile.support_int8 && True(used_types & IR::Type::U8)) { + if (profile.support_int8 && profile.support_uniform_and_storage_buffer_8bit && + True(used_types & IR::Type::U8)) { DefineSsbos(*this, storage_types.U8, &StorageDefinitions::U8, info, binding, U8, sizeof(u8)); DefineSsbos(*this, storage_types.S8, &StorageDefinitions::S8, info, binding, S8, sizeof(u8)); } - if (profile.support_int16 && True(used_types & IR::Type::U16)) { + if (profile.support_int16 && profile.support_uniform_and_storage_buffer_16bit && + True(used_types & IR::Type::U16)) { DefineSsbos(*this, storage_types.U16, &StorageDefinitions::U16, info, binding, U16, sizeof(u16)); DefineSsbos(*this, storage_types.S16, &StorageDefinitions::S16, info, binding, S16, @@ -1447,7 +1449,7 @@ void EmitContext::DefineInputs(const IR::Program& program) { if (info.uses_is_helper_invocation) { is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation); } - if (info.uses_subgroup_mask) { + if (info.uses_subgroup_mask && profile.SupportsSubgroupStage(stage)) { subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR); subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR); subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR); @@ -1461,9 +1463,10 @@ void EmitContext::DefineInputs(const IR::Program& program) { Decorate(subgroup_mask_ge, spv::Decoration::Flat); } } - if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || - (profile.warp_size_potentially_larger_than_guest && - (info.uses_subgroup_vote || info.uses_subgroup_mask))) { + if ((info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || + (profile.warp_size_potentially_larger_than_guest && + (info.uses_subgroup_vote || info.uses_subgroup_mask))) && + profile.SupportsSubgroupStage(stage)) { AddCapability(spv::Capability::GroupNonUniform); subgroup_local_invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index e95778a0de..0197a5f1d7 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -10,6 +10,8 @@ namespace Shader { +enum class Stage : u32; + struct Profile { u32 supported_spirv{0x00010000}; bool unified_descriptor_binding{}; @@ -32,6 +34,7 @@ struct Profile { bool support_fp64_signed_zero_nan_preserve{}; bool support_explicit_workgroup_layout{}; bool support_vote{}; + u32 supported_subgroup_stages{0x7F}; bool support_viewport_index_layer_non_geometry{}; bool support_viewport_mask{}; bool support_typeless_image_loads{}; @@ -95,6 +98,10 @@ struct Profile { u64 min_ssbo_alignment{}; u32 max_user_clip_distances{}; + + bool SupportsSubgroupStage(Stage stage) const { + return (supported_subgroup_stages & (1u << static_cast(stage))) != 0; + } }; } // namespace Shader diff --git a/src/video_core/host1x/codecs/decoder.cpp b/src/video_core/host1x/codecs/decoder.cpp index c75059db6f..1d6d3e8af6 100644 --- a/src/video_core/host1x/codecs/decoder.cpp +++ b/src/video_core/host1x/codecs/decoder.cpp @@ -20,58 +20,54 @@ Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::Nv Decoder::~Decoder() = default; -void Decoder::Decode() { - if (!initialized) { +void Decoder::SetFrameDimensions(s32 width, s32 height) { + if (width <= 0 || height <= 0) { + frame_dimensions.reset(); return; } + frame_dimensions = FFmpeg::FrameDimensions{width, height}; +} - const auto packet_data = ComposeFrame(); - // Send assembled bitstream to decoder. - if (!decode_api.SendPacket(packet_data)) { +void Decoder::Decode() { + if (!initialized) { return; } - // Only receive/store visible frames. - if (vp9_hidden_frame) { - return; + const auto packet_data = ComposeFrame(); + 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 { + std::tie(offsets.luma, std::ignore) = GetProgressiveOffsets(); } - // Receive output frames from decoder. - auto frame = decode_api.ReceiveFrame(); - - if (!frame) { + // Send assembled bitstream to decoder. + if (!decode_api.SendPacket(packet_data, offsets, GetFrameDimensions())) { 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); - } - + auto push = [&](u64 luma, std::shared_ptr frame) { if (UsingDecodeOrder()) { - host1x.frame_queue.PushDecodeOrder(id, luma_top, std::move(frame)); - host1x.frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy)); + host1x.frame_queue.PushDecodeOrder(id, luma, std::move(frame)); } else { - host1x.frame_queue.PushPresentOrder(id, luma_top, std::move(frame)); - host1x.frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy)); + host1x.frame_queue.PushPresentOrder(id, luma, std::move(frame)); } - } else { - auto [luma_offset, chroma_offset] = GetProgressiveOffsets(); + }; - if (!frame.get()) { - LOG_ERROR(HW_GPU, "Nvdec {} failed to decode progressive frame for luma {:#X}", id, - luma_offset); + while (auto result = decode_api.ReceiveFrame()) { + auto& [frame, frame_offsets] = *result; + if (!frame) { + continue; } - - if (UsingDecodeOrder()) { - host1x.frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame)); + 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 { - host1x.frame_queue.PushPresentOrder(id, luma_offset, std::move(frame)); + push(frame_offsets.luma, std::move(frame)); } } } diff --git a/src/video_core/host1x/codecs/decoder.h b/src/video_core/host1x/codecs/decoder.h index 9fca89aa40..2cc6d8dce6 100644 --- a/src/video_core/host1x/codecs/decoder.h +++ b/src/video_core/host1x/codecs/decoder.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -46,12 +47,19 @@ protected: virtual std::tuple GetInterlacedOffsets() = 0; virtual bool IsInterlaced() = 0; + void SetFrameDimensions(s32 width, s32 height); + + std::optional 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 frame_dimensions; }; } // namespace Tegra diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp index f439ac3828..e3a6f5d182 100644 --- a/src/video_core/host1x/codecs/h264.cpp +++ b/src/video_core/host1x/codecs/h264.cpp @@ -52,6 +52,10 @@ bool H264::IsInterlaced() { std::span 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(params.pic_width_in_mbs) * 16, + static_cast(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); diff --git a/src/video_core/host1x/codecs/vp8.cpp b/src/video_core/host1x/codecs/vp8.cpp index 00fe6e4499..33d781816a 100644 --- a/src/video_core/host1x/codecs/vp8.cpp +++ b/src/video_core/host1x/codecs/vp8.cpp @@ -35,6 +35,7 @@ std::tuple VP8::GetInterlacedOffsets() { std::span 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); diff --git a/src/video_core/host1x/codecs/vp9.cpp b/src/video_core/host1x/codecs/vp9.cpp index 1085e08939..dcc6261e5c 100644 --- a/src/video_core/host1x/codecs/vp9.cpp +++ b/src/video_core/host1x/codecs/vp9.cpp @@ -841,6 +841,7 @@ std::span 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 diff --git a/src/video_core/host1x/ffmpeg.cpp b/src/video_core/host1x/ffmpeg.cpp index 88df6695d8..783259ccfc 100644 --- a/src/video_core/host1x/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg.cpp @@ -4,6 +4,10 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include + #include "common/assert.h" #include "common/logging.h" #include "common/scope_exit.h" @@ -84,6 +88,52 @@ std::string AVError(int errnum) { return errbuf; } +#if defined(__ANDROID__) +size_t FindNalStartCode(std::span 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 ExtractH264ParameterSetExtradata(std::span packet) { + std::vector 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 data) { @@ -118,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 { @@ -214,6 +281,10 @@ 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; +#if defined(__ANDROID__) + m_codec_context->flags |= AV_CODEC_FLAG_LOW_DELAY; + m_codec_context->flags2 |= AV_CODEC_FLAG2_FAST; +#endif } DecoderContext::~DecoderContext() { @@ -227,15 +298,25 @@ 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 extradata) { + if (!extradata.empty()) { + av_freep(&m_codec_context->extradata); + m_codec_context->extradata = static_cast( + 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(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; } - if (!m_codec_context->hw_device_ctx) { - LOG_INFO(HW_GPU, "Using FFmpeg CPU decoder"); - } + LOG_INFO(HW_GPU, "Using decoder {}", decoder.GetCodec()->name); return true; } @@ -281,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) { @@ -288,29 +376,90 @@ bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) { m_decoder.emplace(codec); m_decoder_context.emplace(*m_decoder); + bool is_mediacodec = false; +#if defined(__ANDROID__) + const std::string_view decoder_name = m_decoder->GetCodec() ? m_decoder->GetCodec()->name : ""; + is_mediacodec = decoder_name == "h264_mediacodec" || + decoder_name == "vp8_mediacodec" || + decoder_name == "vp9_mediacodec"; +#endif + // Enable GPU decoding if requested. - if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) { + if (!is_mediacodec && + Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) { m_hardware_context.emplace(); m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder); } +#if defined(__ANDROID__) + m_defer_android_mediacodec_open = is_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 packet_data) { +bool DecodeApi::SendPacket(std::span packet_data, const FrameOffsets& offsets, + std::optional dimensions) { + if (!m_opened) { + std::vector 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; + } + if (!offsets.hidden) { + 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 DecodeApi::ReceiveFrame() { - // Receive raw frame from decoder. - return m_decoder_context->ReceiveFrame(); +std::optional 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}; } } diff --git a/src/video_core/host1x/ffmpeg.h b/src/video_core/host1x/ffmpeg.h index fdb6908bb6..00cd3f5d4d 100644 --- a/src/video_core/host1x/ffmpeg.h +++ b/src/video_core/host1x/ffmpeg.h @@ -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 extradata = {}); bool SendPacket(const Packet& packet); std::shared_ptr 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 packet_data); - std::shared_ptr ReceiveFrame(); + bool SendPacket(std::span packet_data, const FrameOffsets& offsets, + std::optional dimensions = std::nullopt); + + struct DecodedFrame { + std::shared_ptr frame; + FrameOffsets offsets; + }; + std::optional ReceiveFrame(); private: std::optional m_decoder; std::optional m_decoder_context; std::optional m_hardware_context; + bool m_opened{}; + bool m_defer_android_mediacodec_open{}; + bool m_needs_h264_extradata{}; + s64 m_next_pts{}; + std::queue m_pending_offsets; }; } // namespace FFmpeg diff --git a/src/video_core/host1x/nvdec.cpp b/src/video_core/host1x/nvdec.cpp index f2e5c358d8..5dbc6a417e 100644 --- a/src/video_core/host1x/nvdec.cpp +++ b/src/video_core/host1x/nvdec.cpp @@ -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) { diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp index f7830a3a45..6514508947 100644 --- a/src/video_core/host1x/vic.cpp +++ b/src/video_core/host1x/vic.cpp @@ -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{}); diff --git a/src/video_core/host1x/vic.h b/src/video_core/host1x/vic.h index 23f3f66c18..18eff3382f 100644 --- a/src/video_core/host1x/vic.h +++ b/src/video_core/host1x/vic.h @@ -628,6 +628,7 @@ private: s32 id; s32 nvdec_id{-1}; + bool has_decoded_frame{}; u32 syncpoint; }; diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 0523a040c8..882372aee2 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -242,6 +242,10 @@ inline void PushImageDescriptors(TextureCache& texture_cache, VideoCore::Surface::IsPixelFormatInteger(image_view.format)) { vk_sampler = sampler.HandleWithNearestFilter(); } + if (desc.is_depth && sampler.HasDepthComparison() && + !image_view.SupportsDepthComparison()) { + vk_sampler = sampler.HandleWithoutDepthComparison(); + } guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); const bool element_rescaled{texture_cache.IsRescaling(image_view)}; is_rescaled |= element_rescaled; diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index f361c34f44..edc0283d9a 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -746,10 +746,8 @@ void BlockLinearUnswizzle3DPass::Unswizzle( const u32 MAX_BATCH_SLICES = (std::min)(z_count, image.info.size.depth); - if (!image.has_compute_unswizzle_buffer) { - // Allocate exactly what this batch needs - image.AllocateComputeUnswizzleBuffer(MAX_BATCH_SLICES); - } + // Allocate or grow to cover this batch's slice count + image.AllocateComputeUnswizzleBuffer(MAX_BATCH_SLICES); ASSERT(swizzles.size() == 1); const auto& sw = swizzles[0]; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 5525ce0e95..006bfc0a09 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -22,6 +22,7 @@ #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/gpu_logging/gpu_logging.h" +#include "common/logging.h" #include "common/settings.h" namespace Vulkan { @@ -36,10 +37,10 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk Common::ThreadWorker* thread_worker, PipelineStatistics* pipeline_statistics, VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, - vk::ShaderModule spv_module_) + vk::ShaderModule spv_module_, u64 shader_hash_) : device{device_}, pipeline_cache(pipeline_cache_), guest_descriptor_queue{guest_descriptor_queue_}, info{info_}, - spv_module(std::move(spv_module_)) { + shader_hash{shader_hash_}, spv_module(std::move(spv_module_)) { if (shader_notify) { shader_notify->MarkShaderBuilding(); } @@ -68,7 +69,7 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk if (device.IsKhrPipelineExecutablePropertiesEnabled() && Settings::values.renderer_debug.GetValue()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } - pipeline = device.GetLogical().CreateComputePipeline(VkComputePipelineCreateInfo{ + const VkComputePipelineCreateInfo compute_ci{ .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, .pNext = nullptr, .flags = flags, @@ -85,7 +86,20 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk .layout = *pipeline_layout, .basePipelineHandle = 0, .basePipelineIndex = 0, - }, *pipeline_cache); + }; + try { + pipeline = device.GetLogical().CreateComputePipeline(compute_ci, *pipeline_cache); + } catch (const vk::Exception& exception) { + LOG_CRITICAL(Render_Vulkan, "Adreno rejected compute shader {:016X}: {}", shader_hash, + exception.what()); + std::scoped_lock lock{build_mutex}; + is_built = true; + build_condvar.notify_one(); + if (shader_notify) { + shader_notify->MarkShaderComplete(); + } + return; + } // Log compute pipeline creation if (GPU::Logging::IsActive()) { @@ -239,6 +253,9 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, const bool is_rescaling = !info.texture_descriptors.empty() || !info.image_descriptors.empty(); scheduler.Record([this, descriptor_data, is_rescaling, rescaling_data = rescaling.Data()](vk::CommandBuffer cmdbuf) { + if (!pipeline) { + return; + } cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); if (!descriptor_set_layout) { return; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index 1feeed4840..c9704b7c6c 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -37,7 +37,7 @@ public: Common::ThreadWorker* thread_worker, PipelineStatistics* pipeline_statistics, VideoCore::ShaderNotify* shader_notify, const Shader::Info& info, - vk::ShaderModule spv_module); + vk::ShaderModule spv_module, u64 shader_hash); ComputePipeline& operator=(ComputePipeline&&) noexcept = delete; ComputePipeline(ComputePipeline&&) noexcept = delete; @@ -53,6 +53,7 @@ private: vk::PipelineCache& pipeline_cache; GuestDescriptorQueue& guest_descriptor_queue; Shader::Info info; + u64 shader_hash{}; u32 num_descriptor_entries{}; VideoCommon::ComputeUniformBufferSizes uniform_buffer_sizes{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 2eea66802d..6ca9fe4c71 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -353,6 +353,20 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, serialization_thread(1, "VkPipelineSerialization") { const auto& float_control{device.FloatControlProperties()}; const VkDriverId driver_id{device.GetDriverID()}; + const VkShaderStageFlags subgroup_stages{device.GetSubgroupSupportedStages()}; + const auto subgroup_stage_bit{[subgroup_stages](VkShaderStageFlags flag, Shader::Stage stage) { + return (subgroup_stages & flag) != 0 ? (1u << static_cast(stage)) : 0u; + }}; + const u32 supported_subgroup_stages{ + subgroup_stage_bit(VK_SHADER_STAGE_VERTEX_BIT, Shader::Stage::VertexA) | + subgroup_stage_bit(VK_SHADER_STAGE_VERTEX_BIT, Shader::Stage::VertexB) | + subgroup_stage_bit(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, + Shader::Stage::TessellationControl) | + subgroup_stage_bit(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, + Shader::Stage::TessellationEval) | + subgroup_stage_bit(VK_SHADER_STAGE_GEOMETRY_BIT, Shader::Stage::Geometry) | + subgroup_stage_bit(VK_SHADER_STAGE_FRAGMENT_BIT, Shader::Stage::Fragment) | + subgroup_stage_bit(VK_SHADER_STAGE_COMPUTE_BIT, Shader::Stage::Compute)}; profile = Shader::Profile{ .supported_spirv = device.SupportedSpirvVersion(), .unified_descriptor_binding = true, @@ -382,6 +396,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE, .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(), .support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT), + .supported_subgroup_stages = supported_subgroup_stages, .support_viewport_index_layer_non_geometry = device.IsExtShaderViewportIndexLayerSupported(), .support_viewport_mask = device.IsNvViewportArray2Supported(), @@ -930,7 +945,8 @@ std::unique_ptr PipelineCache::CreateComputePipeline( Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; return std::make_unique(device, scheduler, vulkan_pipeline_cache, descriptor_pool, guest_descriptor_queue, thread_worker, statistics, - &shader_notify, program.info, std::move(spv_module)); + &shader_notify, program.info, std::move(spv_module), + key.unique_hash); } catch (const Shader::Exception& exception) { LOG_ERROR(Render_Vulkan, "{}", exception.what()); diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 8d352f3b73..709f65dc25 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -236,7 +236,8 @@ public: } PauseCounter(); const auto driver_id = device.GetDriverID(); - if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || + driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { pending_sync.clear(); sync_values_stash.clear(); return; @@ -1520,7 +1521,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku auto driver_id = impl->device.GetDriverID(); const bool is_gpu_high = Settings::IsGPULevelHigh(); - if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { EndHostConditionalRendering(); return true; } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index ac4bc29905..960dafb99d 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1635,9 +1635,6 @@ Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBas Image::~Image() = default; void Image::AllocateComputeUnswizzleBuffer(u32 max_slices) { - if (has_compute_unswizzle_buffer) - return; - using VideoCore::Surface::BytesPerBlock; const u32 block_bytes = BytesPerBlock(info.format); // 8 for BC1, 16 for BC6H @@ -1654,7 +1651,12 @@ void Image::AllocateComputeUnswizzleBuffer(u32 max_slices) { static_cast(blocks_y) * static_cast(blocks_z); - compute_unswizzle_buffer_size = block_count * block_bytes; + const VkDeviceSize required_size = block_count * block_bytes; + if (has_compute_unswizzle_buffer && required_size <= compute_unswizzle_buffer_size) { + return; + } + + compute_unswizzle_buffer_size = required_size; VkBufferCreateInfo ci{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, @@ -2152,6 +2154,15 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI if (uses_widened_astc_format) { format_info.format = VK_FORMAT_R32G32B32A32_SFLOAT; } + if (device->ApiVersion() >= VK_API_VERSION_1_3) { + const VkFormatProperties3 properties3 = + device->GetPhysical().GetFormatProperties3(format_info.format); + supports_depth_comparison = + (properties3.optimalTilingFeatures & + VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) != 0; + } else { + supports_depth_comparison = true; + } const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format); const VkImageUsageFlags image_usage = image.UsageFlags(); const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage; @@ -2290,7 +2301,7 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type, if (uses_widened_astc_format) { info.format = VK_FORMAT_R32G32B32A32_SFLOAT; } - typeless_storage_view = MakeView(info.format, VK_IMAGE_ASPECT_COLOR_BIT); + typeless_storage_view = MakeView(info.format, VK_IMAGE_ASPECT_COLOR_BIT, texture_type); } return *typeless_storage_view; } @@ -2301,7 +2312,7 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type, auto& views{is_signed ? storage_views->signeds : storage_views->unsigneds}; auto& view{views[size_t(texture_type)]}; if (!view) - view = MakeView(Format(image_format), VK_IMAGE_ASPECT_COLOR_BIT); + view = MakeView(Format(image_format), VK_IMAGE_ASPECT_COLOR_BIT, texture_type); return *view; } return VK_NULL_HANDLE; @@ -2311,13 +2322,28 @@ bool ImageView::IsRescaled() const noexcept { return (*slot_images)[image_id].IsRescaled(); } -vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask) { +vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask, + std::optional texture_type) { + VkImageViewType view_type = ImageViewType(type); + VkImageSubresourceRange subresource_range = MakeSubresourceRange(aspect_mask, range); + if (texture_type) { + view_type = ImageViewType(*texture_type); + switch (view_type) { + case VK_IMAGE_VIEW_TYPE_1D_ARRAY: + case VK_IMAGE_VIEW_TYPE_2D_ARRAY: + case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: + break; + default: + subresource_range.layerCount = 1; + break; + } + } return device->GetLogical().CreateImageView({ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, .flags = 0, .image = image_handle, - .viewType = ImageViewType(type), + .viewType = view_type, .format = vk_format, .components{ .r = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -2325,7 +2351,7 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_ .b = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY, }, - .subresourceRange = MakeSubresourceRange(aspect_mask, range), + .subresourceRange = subresource_range, }); } @@ -2376,7 +2402,8 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t min_filter == VK_FILTER_LINEAR || mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR}; - const auto create_sampler = [&](const f32 anisotropy, bool force_nearest) { + const auto create_sampler = [&](const f32 anisotropy, bool force_nearest, + bool disable_compare = false) { return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = pnext, @@ -2391,7 +2418,8 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t .anisotropyEnable = static_cast(!force_nearest && anisotropy > 1.0f ? VK_TRUE : VK_FALSE), .maxAnisotropy = force_nearest ? 1.0f : anisotropy, - .compareEnable = tsc.depth_compare_enabled, + .compareEnable = disable_compare ? VK_FALSE + : static_cast(tsc.depth_compare_enabled), .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), @@ -2410,6 +2438,9 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t if (has_linear_filtering) { sampler_nearest = create_sampler(1.0f, true); } + if (tsc.depth_compare_enabled) { + sampler_noncompare = create_sampler(max_anisotropy, false, true); + } } Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span color_buffers, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 027143fdbc..13f51555a3 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -356,6 +356,10 @@ public: return samples; } + [[nodiscard]] bool SupportsDepthComparison() const noexcept { + return supports_depth_comparison; + } + [[nodiscard]] GPUVAddr GpuAddr() const noexcept { return gpu_addr; } @@ -370,7 +374,8 @@ private: std::array unsigneds; }; - [[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask); + [[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask, + std::optional texture_type = std::nullopt); const Device* device = nullptr; const SlotVector* slot_images = nullptr; @@ -388,6 +393,7 @@ private: u32 buffer_size = 0; bool uses_widened_astc_format = false; + bool supports_depth_comparison = false; }; class ImageAlloc : public VideoCommon::ImageAllocBase {}; @@ -416,10 +422,19 @@ public: return static_cast(sampler_nearest); } + [[nodiscard]] VkSampler HandleWithoutDepthComparison() const noexcept { + return *sampler_noncompare; + } + + [[nodiscard]] bool HasDepthComparison() const noexcept { + return static_cast(sampler_noncompare); + } + private: vk::Sampler sampler; vk::Sampler sampler_default_anisotropy; vk::Sampler sampler_nearest; + vk::Sampler sampler_noncompare; }; struct TextureCacheParams { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index ab6395cef7..8bddca0c70 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -300,6 +300,10 @@ ankerl::unordered_dense::map GetFormatProperties(v VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + VK_FORMAT_EAC_R11_UNORM_BLOCK, + VK_FORMAT_EAC_R11_SNORM_BLOCK, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK, }; ankerl::unordered_dense::map format_properties; for (const auto format : formats) { @@ -502,9 +506,15 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR if (is_qualcomm) { LOG_WARNING(Render_Vulkan, "Qualcomm drivers require scaled vertex format emulation"); must_emulate_scaled_formats = true; - LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken CustomBorderColor."); + LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken custom border color."); RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken border color swizzle."); + RemoveExtensionFeature(extensions.border_color_swizzle, features.border_color_swizzle, + VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME); + LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken color write enable."); + RemoveExtensionFeature(extensions.color_write_enable, features.color_write_enable, + VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader float controls."); RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader atomic int64."); @@ -513,6 +523,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR features.shader_atomic_int64.shaderBufferInt64Atomics = false; features.shader_atomic_int64.shaderSharedInt64Atomics = false; features.features.shaderInt64 = false; + LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken storage buffer access."); + features.bit8_storage.storageBuffer8BitAccess = false; + features.bit8_storage.uniformAndStorageBuffer8BitAccess = false; + features.bit16_storage.storageBuffer16BitAccess = false; + features.bit16_storage.uniformAndStorageBuffer16BitAccess = false; #if defined(__ANDROID__) && defined(ARCHITECTURE_arm64) // BCn patching only safe on Android 9+ (API 28+). Older versions crash on driver load. diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index a98717bc3e..db92df70a6 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -399,6 +399,16 @@ FN_MAX_LIMIT_LIST return features.bit16_storage.uniformAndStorageBuffer16BitAccess; } + /// Returns true if the device supports reading 8-bit values from a storage buffer. + bool IsStorageBuffer8BitAccessSupported() const { + return features.bit8_storage.storageBuffer8BitAccess; + } + + /// Returns true if the device supports reading 16-bit values from a storage buffer. + bool IsStorageBuffer16BitAccessSupported() const { + return features.bit16_storage.storageBuffer16BitAccess; + } + /// Returns true if the device supports binding multisample images as storage images. bool IsStorageImageMultisampleSupported() const { return features.features.shaderStorageImageMultisample; @@ -419,6 +429,10 @@ FN_MAX_LIMIT_LIST return properties.subgroup_properties.supportedOperations & feature; } + VkShaderStageFlags GetSubgroupSupportedStages() const { + return properties.subgroup_properties.supportedStages; + } + /// Returns the maximum number of push descriptors. u32 MaxPushDescriptors() const { return properties.push_descriptor.maxPushDescriptors; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index e5d5e93426..4943705859 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -301,6 +301,7 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept { X(vkDestroyDebugReportCallbackEXT); X(vkDestroySurfaceKHR); X(vkGetPhysicalDeviceFeatures2); + X(vkGetPhysicalDeviceFormatProperties2); X(vkGetPhysicalDeviceProperties2); X(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); X(vkGetPhysicalDeviceSurfaceFormatsKHR); @@ -916,6 +917,23 @@ VkFormatProperties PhysicalDevice::GetFormatProperties(VkFormat format) const no return properties; } +VkFormatProperties3 PhysicalDevice::GetFormatProperties3(VkFormat format) const noexcept { + VkFormatProperties3 properties3{ + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3, + .pNext = nullptr, + .linearTilingFeatures = 0, + .optimalTilingFeatures = 0, + .bufferFeatures = 0, + }; + VkFormatProperties2 properties2{ + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + .pNext = &properties3, + .formatProperties = {}, + }; + dld->vkGetPhysicalDeviceFormatProperties2(physical_device, format, &properties2); + return properties3; +} + std::vector PhysicalDevice::EnumerateDeviceExtensionProperties() const { u32 num; dld->vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &num, nullptr); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index e50856533e..ef975e2bda 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -180,6 +180,7 @@ struct InstanceDispatch { PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr{}; PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2{}; PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{}; + PFN_vkGetPhysicalDeviceFormatProperties2 vkGetPhysicalDeviceFormatProperties2{}; PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{}; PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{}; PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{}; @@ -1130,6 +1131,8 @@ public: VkFormatProperties GetFormatProperties(VkFormat) const noexcept; + VkFormatProperties3 GetFormatProperties3(VkFormat) const noexcept; + std::vector EnumerateDeviceExtensionProperties() const; std::vector GetQueueFamilyProperties() const;